3. CHƯƠNG 3: XÂY DỰNG ỨNG DỤNG GTALK
3.1.4 Giới thiệu về gói thư viện asmack.jar
Có nhiều gói thư viện thứ 2 có sẵn để phát triển ứng dụng IM client. Một trong số những thư viện được sử dụng nhiều nhất cho XMPP client libraries là Smack API, đây là thư viện java thuần túy cho phép phát triển IM clients
Phiên bản được hỗ trợ mới nhất là aSmack 3.2.2 bởi nhà phát triển Samsum 3.2 Xây dựng project XMPPChatDemo
Ứng dụng này thực hiện các nhiệm vụ sau: 1. Kết nối đến Gtalk server
2. Đăng nhập vào Gtalk server
3. Thiết lập sự hiện diện của người dùng 4. Get Rosters
5. Gửi tin nhắn
6. Nhận tin nhắn
45
Layout: Tạo layout như hình, ta có một đối tượng EditText để nhập địa chỉ đích, một đối tượng EditText để nhập nội dung tin nhắn, một đối tượng Button để gửi tin và một ListView để hiển thị nội dung. Trong phần thiết lập ListView đã được trình bày ở phần trước
Hình 3.2.1 Layout ChatDemo
46
Hình 3.2.2 Cấu trúc MainActivity
Những đoạn code chính cần quan tâm :
Khi sự kiện người dụng ấn vào nút nhấn ta tiến hành đọc nội dụng nhập vào để
lấy địa chỉ đích bằng phương thức getText() đối với đối tượng recipient mà ta đã
định nghĩa trong phần layout. Đưa nội dung tin nhắn vào đối tượng text. Đóng gói dữ liệu thành đối tượng message. Đối tượng này sẽ được gửi đi bằng phương thức
sendPacke(). Thực hiện kết nối bằng phương thức connect().
String to = recipient.getText().toString(); String text = textMessage.getText().toString(); Message msg = new Message(to, Message.Type.chat); msg.setBody(text);
if (connection != null) {
connection.sendPacket(msg);
messages.add(connection.getUser() + ":");
messages.add(text); setListAdapter(); } } }); connect(); }
47
Kết nối đến server
Sử dụng aSmack SPI để kết nối đến server bằng đoạn code sau:Class XMPP được sử dụng để tạo kết nối với server XMPP được xác định bằng ConnectionConfiguration class, nó sẽ thiết lập bởi tham số truyền vào là tên server.
Để ngắt kết nối sử dụng phương thức disconnect().
Đăng nhập vào Server
Một khi đã thiết lập được kết nối, người dùng sẽ đăng nhập vào server với
username và password sử dụng phương thức login() của lớp Connection class
public static final String HOST = "talk.google.com";
public static final int PORT = 5222;
public static final String SERVICE = "gmail.com";
// Create a connection
ConnectionConfiguration connConfig = new ConnectionConfiguration(
HOST, PORT, SERVICE); XMPPConnection connection = new XMPPConnection(connConfig);
try {
connection.connect();
Log.i("XMPPChatDemoActivity","Connected to " + connection.getHost());
} catch (XMPPException ex) {
Log.e("XMPPChatDemoActivity", "Failed to connect to "
+ connection.getHost());
Log.e("XMPPChatDemoActivity", ex.toString()); setConnection(null);
// SASLAuthentication.supportSASLMechanism("PLAIN", 0);
connection.login(USERNAME, PASSWORD);
48
Thiết lập chế độ hiển thị
Sau khi đăng nhập, người dùng sẽ thiết lập trạng thái ẩn hiện với những người khác trong chat list. Đoạn code dưới cho biết cách thiết lập sự hiện diện. Đối tượng hiện diện được tạo với một trạng thái “available”. Trạng thái hiện diện này được gửi bằng một gói dữ liệu sau đó bởi phương thức sendPacket() của Connection class
Getting Roster
Class Roster thực hiện các công việc sau :
- Theo dõi sự hiện diện của người dùng khác
- Cho phép người dùng được tổ chức thành nhóm ví dụ như bạn bè hoặc
gia đình
- Tìm thấy tất cả nhưng danh mục và nhóm và ta có - Lấy trạng thái hiện diện của mỗi người dùng
Đoạn code trước cho ta biết trạng thái của người dùng tại thời điểm đăng nhập, nhưng khi có một người dùng khác đăng nhập tại thời điểm hiện tại thì ta cần phải
sử dụng bộ lắng nghe sự kiện RosterListener(interface) Phương thức sau sẽ được gọi khi có bất kì sự thay đổi nào của roster.
// Set the status to available
Presence presence = new Presence(Presence.Type.available); connection.sendPacket(presence);
setConnection(connection);
Roster roster = connection.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
Presence entryPresence = roster.getPresence(entry.getUser());
Status:"+entryPresence.getStatus());
Presence.Type type = entryPresence.getType();
if (type == Presence.Type.available) }
49
Gửi tin nhắn
Có 2 cách để gửi tin nhắn
- Sử dụng phương thức sendPacket(msg) của class XMPPconnection.
- Sử dụng class chatManager như đoạn code sau :
Đối tượng ChatManager được tạo ra từ XMPPConnection sử dụng phương thức getChatManager(). Chatmanager theo dõi tất cả các cuộc chat hiện tại.
Nhận tin nhắn
Việc nhận tin nhắn từ người dùng khác được thực hiện bởi :
- Cơ chế thăm dò ý kiến được cung cấp thông qua việc sử dụng các lớp PacketCollector.
Roster roster;
roster.addRosterListener(new RosterListener() { @Override
public void presenceChanged(Presence presence) {
//Called when the presence of a roster entry is changed
}
@Override
public void entriesUpdated(Collection<String> arg0) {
// Called when a roster entries are updated.
} @Override
public void entriesDeleted(Collection<String> arg0) {
// Called when a roster entries are removed.
}
@Override
public void entriesAdded(Collection<String> arg0) {
// TODO Auto-generated method stub
}
ChatManager chatmanager = connection.getChatManager();
Chat newChat = chatmanager.createChat("abc@gmail.com", new
MessageListener() {
// Receiving Messages
public void processMessage(Chat chat, Message message) {
Message outMsg = new Message(message.getBody());
//Send Message object
newChat.sendMessage(outMsg);
//Send String as Message
50
- Cơ chế bất đồng bộ thông qua việc sử dụng bộ lắng nghe sự kiện
PacketListener()
Manifest: trong file manifest ngoài những thiết lập căn bản ta cẩn chắc chắn rằng permission đã được add
3.2.2 Demo
Khởi động 2 máy ảo emulator chạy đồng thời máy A (5554) và máy B(5556) Build và nạp ứng dụng vào máy A với username :hieupro10261@gmail.com Quan sát màn hình longcat cho ta thấy được các tài khoản trong danh mục và tên users
public void setConnection(XMPPConnection connection) {
this.connection = connection;
if (connection != null) {
// Add a packet listener to get messages sent to us
PacketFilter filter = new
MessageTypeFilter(Message.Type.chat);
connection.addPacketListener(new PacketListener() { @Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
if (message.getBody() != null) { String fromName =
StringUtils.parseBareAddress(message.getFrom());
Log.i("XMPPChatDemoActivity", "Text Recieved " + message.getBody()+ " from " + fromName );
messages.add(fromName + ":"); messages.add(message.getBody());
// Add the incoming message to the list view
mHandler.post(new Runnable() {
public void run() {
setListAdapter(); }
51
Buid và nạp ứng dụng cho máy B với username:hieubk4cqt2009@gmail.com Quan sát longcat thu được :
52
Như vậy máy A đang đang nhập với user 1:
237k61gtp2pve02joytadqu7kx@public.talk.google.com
Và máy B đang đăng nhập với user 2:
24qshpfxth58y1k0z3vevtg3n6@public.talk.google.com Đối với máy A nhập vào ô chat with là user 2 Đối với máy B nhập vào ô chat with là user 1 Thu được kết quả sau:
53
54
4. CHƯƠNG 4: XÂY DỰNG ỨNG DỤNG STREAM AUDIO VÀ VIDEO VÀ VIDEO
4.1 Giới thiệu về Audio trên Android
Bất kì thiết bị smartphone nào ngày nay đều có khả năng chạy audio playback tương tự như các thiết bị media hoặc MP3 player.Dĩ nhiên, thiết bị Android cũng không ngoại lệ.Cho phép xây dựng các ứng dụng music player, audio book, podcast, hoặc bất kì ứng dụng nào liên quan tới audio playback
4.1.1 Định dạng hỗ trợ Audio
Android hỗ trợ nhiều định dạng file audio và giải mã (codecs for playback) tuy nhiên nó chỉ hỗ trợ vài định dạng recording .
AAC: Advanced Audio Coding codec là những định dạng như .m4a hoặc 3.gp. AAC là một chuẩn được sử dụng bở Ipod và các thiết bị chơi nhạc khác. Android hỗ trợ định dạng audio là MPEG-4 files và 3GP files
MP3: MPEG-1 Audio Layer 3, .mp3 file. MP3, đây là dạng được dùng phổ biến nhất được hỗ trợ trong Android cho phép đọc những file online trên nhiều web sites và music stores
AMR: Adaptive Multi-Rate codec những định dạng như .3gp hoặc .amr . AMR là chuẩn được sử dụng trong truyền thông và những ứng dụng voice calling trên điện thoại và được hỗ trợ phát triển bởi nhà sản xuất.
Ogg: Ogg Vorbis, .ogg files. Là một dạng mã nguồn mở, chất lượng của nó cũng giống như định dạng MP3 và ACC.
PCM:Pluse Code Modulation thông thường là những file .wav. PCM là công nghệ sử dụng cho lưu trữ trên máy tính và thiết bị digital audio khác 4.1.2 Khởi chạy Audio thông qua Intent
Chúng ta sẽ xây dựng một ứng dụng Music đơn giản, có thể chạy tất cả các files mà Android hỗ trợ. Đơn giản ta chỉ cần gọi chương trình có sẵn trong hệ điều hành và sử dụng nó. Đầu tiên ta thiết lập giao diện cho ứng dụng là như sau:
55
Hình: 1Play audio layout
Trong Activity chính thiết lập sự kiện cho nút button
Khi button được nhấn sự kiện onClick sẽ xảy ra, trong hàm này ta sẽ gọi App có sẵn để chạy file .Định danh ACTION_VIEW để gọi đến app có sẵn .Tạo đối tượng sdcard để lấy đường dẫn các file trong sdcard sau đó lấy đường dẫn đến file cần mở .Đối với đối tượng intent ta sử dụng phương thức để cài đặt đường dẫn Uri và kiểu định dạng cho nó và cuối cùng kích hoạt Intent.
Trong file manifest ta phải khai báo để cho phép Activity gọi App khác
playButton = (Button) this.findViewById(R.id.Button01);
playButton.setOnClickListener(this);
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
File sdcard = Environment.getExternalStorageDirectory();
File audioFile =new File (sdcard.getPath()+
"/music/goodmorningandroid.mp3");
intent.setDataAndType(Uri.fromFile(audioFile), "audio/mp3"); startActivity(intent);
56
Quan sát Emulator ta có kết quả :
Hình: 2Kết quả chạy audio
4.1.3 Xây dựng một ứng dụng Audio
Dĩ nhiên là ta có thể tự xây dụng một ứng dụng audio riêng, tạo giao diện theo ý thích và lựa chọn các tính năng cần thiết cho ứng dụng. Để làm được điều này, Android cung cấp một class là MediaPlayer. Class này được sử dụng cho playback và điều khiển cho cả audio và video. Ví dụ đơn giản nhất về MediaPlayer là chạy một file audio được đóng gói trong chính ứng dụng. Ta phải tạo một folder tài nguyên tên là raw như sau :
57
Tạo một đối tượng MediaPlayer sử dụng phương thức create của class MediaPlayer với tham số truyền vào là đường dãn tới thư mục chứa file cần mở :
Sau đó sử dụng phương thức start để mở :
Control playback
Class MediaPlayer chứa nhiều class để bắt sự kiện mà MediaPlayer gửi tới.Nó sẽ gọi phương thức onCompletionListener và được cài đặt thông qua phương thức setOnCompletionListener khi mà file audio được chạy xong.
Sau đây em xin trình bày một ví dụ về tạo giao diện điều khiển đơn giản để chạy một file audio được đóng gói sẵn trong ứng dụng.
Tạo giao diện điều khiển như sau :
Hình: 4Layout audio custom
mediaPlayer = MediaPlayer.create(this, R.raw.goodmorningandroid);
58
Sử dụng hàm findViewById , ta truy cập đến đối tượng nút nhấn được định nghĩa trong file Layout XML
Để activity có thể đáp ứng được sự kiện click, ta phải khai báo bộ listener cho 2 nút này :
Tạo một đối tượng theView và khai báo sự kiện chạm tay vào màn hình:
Dưới đây là phương thức onComletion, được gọi khi MediaPlayer hoàn tất việc chạy file audio. Ta sẽ gọi phương thức start để chạy audio sau đó gọi phương thức seekTo đến vị trí đã lưu vào biến position :
Khi người dùng chạm vào màn hình cảm ứng , phương thức onTouch sẽ được gọi. Trong phương thức này chúng ta chỉ quan tâm đến sự kiện ACTION_MOVE, đây là sự kiện khi người dùng chạm và di chuyển ngón tay trên màn hình.
stopButton = (Button) this.findViewById(R.id.StopButton);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
theView = this.findViewById(R.id.theview);
theView.setOnTouchListener(this);
mediaPlayer.start();
@Override
public boolean onTouch(View v, MotionEvent me) {
if (me.getAction() == MotionEvent.ACTION_MOVE) {
if (mediaPlayer.isPlaying()) {
position = (int) (me.getX() *
mediaPlayer.getDuration() / theView
.getWidth());
Log.v("SEEK", "" + position);
59
Cuối cùng, thiết lập sự kiện nút nhấn cho 2 Button :
4.2 Background and Networked Audio 4.2.1 Background Audio Playback 4.2.1 Background Audio Playback
Nếu chúng ta muốn người dùng có thể làm những công việc khác trong khi đang nghe nhạc ? Có một vấn đề gặp phải khi nó được xây dựng bằng activities. Hệ điều hành Android sẽ có thể kill activities khi nó không tương tác với người dùng. Nếu OS kill 1 activity đang chạy audio, thì điều này sẽ gây nên sự bất tiện cho người dùng. Một giải pháp được đưa ra là thay vì sử dụng activities ta sẽ sử dụng Service.
Service
Để chắc chắn rằng audio sẽ tiếp tục được chạy khi ứng dụng không còn tương tác với người dùng nữa ta cần tạo một Service. Đó là một thành phần của các ứng dụng Android mà các tác vụ được chạy ngầm và không tương tác và hiển thị với người dùng.
Có 2 class Service khác nhau được dùng trong Android. Thứ nhất là Local Service. Local Service tồn tại như một phần ứng dụng đặc biệt và được truy cập chỉ bởi ứng dụng đó.Loại thứ 2 là Remote Service. Chúng có thể giao tiếp được với nhau có thể được truy cập và điểu khiển bởi ứng dụng khác
Điều khiển một MediaPlayer trong Service
Để cho phép điều khiển một đối tượng MediaPlayer ta cần phải ràng buộc Activity và Service với nhau. Một khi hoàn tất thì Activity và Service sẽ cùng chạy song song với nhau
4.2.2 Networked Audio
Trong Android có hỗ trợ khá nhiều để chạy nhiều định dạng file audio có sẵn trên network
@Override
public void onClick(View v) {
if (v == stopButton) {
mediaPlayer.pause();
60
61
HTTP audio playback
Ta sẽ xây dựng một ví dụ đơn giản để chạy audio file và truy cập thông qua
HTTP, server có sẵn :
http://www.mobvcasting.com/android/audio/goodmorningandroid.mp3
Trong phương thức onCreate() tạo mới một đối tượng mediaplayer:
Với đối tượng này sử dụng phương thức SetDataSource() để link đến file cần
chạy trên server, sau đó là phương thức prepare() nó sẽ load dữ liệu từ server về máy, chú ý là nó sẽ load toàn bộ file về máy trước khi thực hiện start().Sau đó file này sẽ được chạy như ví dụ trước đã trình bày. Các phương thức này có thể ném ra các ngoại lệ nên phải dùng cú pháp try catch :
Audio playback HTTP sử dụng prepareAsync()
Trong ví dụ trước, chỉ chạy được những file có dung lượng nhỏ, còn đối với những file có dung lượng lớn thì thời gian load toàn bộ file về rất lớn, không đáp ứng được nhu cầu chạy trực tuyến . Một giải pháp để khắc phục điều này là sử dụng phương thức prepareAsync(). Phương thức này sẽ load dữ liệu vào một buffer và khi đầy thì sẽ chuyển vào trạng thái prepared như trong sơ đồ Hình 4.5.
Dưới đây ta sẽ phân tích đoạn code để hiện thực hóa sơ đồ trên : Tạo giao diện layout như sau:
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource("http://www.mobvcasting.com/android/audio
/goodmorningandroid.mp3");
mediaPlayer.prepare();
62
Hình: 6Layout audio HTTP
Trong phương thức onCreate ta khai báo và thiết lập các đối tượng cần thiết:
Nút nhấn, textView, MediaPlayer…
Thiết lập bộ listener cho từng đối tượng:
Gọi phương thức :
stopButton.setOnClickListener(this);
startButton.setOnClickListener(this);
stopButton.setEnabled(false);
startButton.setEnabled(false);
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.setDataSource("http://www.mobvcasting.com/android/audio
63
Xử lí các sự kiện. Khi có nút nhấn tương ứng, ta hiển thị lên màn hình phương thức nào đã được gọi
Trong file manifest phải khai báo quyền sử dụng Internet :
Streaming Audio via HTTP
Phương thức streaming thông qua HTTP được phát triển từ năm 1999 do một công ty tên là Nullsoft, Công ty này xây dựng một phần mềm gọi là WinAMP một trình chơi MP3 thông dụng, và phát triển 1 server hỗ trợ audio streaming sử dụng HTTP. Hiện tại một số lượng lớn các servers và phần mềm playback được tạo ra đều hỗ trợ giao thức ICY, và là một chuẩn cho việc nghe radio online.
Ví dụ về sử dụng và chạy 1 file M3U chứa đường dẫn đến một trạm radio hoặc bất kì M3U file chứa URL thích hợp:
Tạo layout như sau : @Override
public void onClick(View v) {
if (v == stopButton) {
mediaPlayer.pause();
statusTextView.setText("pause called");
startButton.setEnabled(true);
}else if (v == startButton) {
mediaPlayer.start();
statusTextView.setText("start called");
startButton.setEnabled(false);
stopButton.setEnabled(true);
} }
64
Trong phương thức onCreate để tạo các đối tượng sau :
Thiết lập các bộ Listener sự kiện cho các đối tượng:
Khi nút parseButton được nhấn, nó sẽ thực hiện phương thức
parsePlayListFile() mà chúng ta tự định nghĩa. Phương thức này tạo một đối tượng
HttpClient và gửi một yêu cầu đến server và ta sẽ nhân được một đối tượng trả về là HttpResponse. Tiến hành lấy nội dung từ đối tượng này bằng các câu lệnh sau :
parseButton = (Button) this.findViewById(R.id.ButtonParse);
playButton = (Button) this.findViewById(R.id.PlayButton);
stopButton = (Button) this.findViewById(R.id.StopButton);
editTextUrl =(EditText)this.findViewById(R.id.EditTextURL);
Hiển thị đường link lên màn hình edittext:
parseButton.setOnClickListener(this);
playButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
mediaPlayer = new MediaPlayer();
InputStream inputStream = httpResponse.getEntity().getContent(); BufferedReader bufferedReader =new
65
Sử dụng phương thức readLine() cho đối tượng bufferedReader này ta sẽ thu
được dữ liệu dạng String. Nếu dữ liệu bắt đầu là kí tự # thì nó là metada và bỏ qua nó, ta chỉ quan tâm đến dữ liệu bắt đầu là http:// mà thôi, cứ mỗi khi lấy được 1 đường dẫn url ta lại add nó vào một đối tượng là playListItems. Như vậy quá trình phân tính file đã xong, ta cho phép nút nhấn playButton.
Khi người dùng nhấn playButton phương thức playPlaylistItems() được gọi.