Sunday, July 8, 2012

Getting MPMoviePlayerController to Work with iOS4, 3.2 (iPad) and Earlier Versions of iPhone SDK

The API and overall approach for working with MPMoviePlayerController has changed just enough in the 3.2 SDK (iPad) and iOS4 SDK to cause working applications in earlier releases to be problematic when running on later SDKs. In this post I’ll walk through a short example of a movie player application that will work with 3.x, 3.2 and 4.0 SDKs.
In the next post, Display iPhone Movies in Portrait Mode, I’ll show how to create a movie player that runs in a view that is not fullscreen as well as how to show a few lines of code to display a movie in portrait mode – the one caveat here is that both of the tips in the second post will apply only to OS versions 3.2 and up.


What’s Changed with MPMoviePlayerController in 3.2 and 4.0?
There are any number of changes in the MPMoviePlayerController, however, a few standout as potential roadblocks to getting a movie to display:
- In 3.1 and earlier versions, MPMoviePlayerController was full-screen only. Playing a movie was straight-forward, create a player, initialize with a file (path or URL) and call a method to start playback – the rest was taken care of for you.
- With 3.2 and later, movies can playback in fullscreen or a custom view, as well as portrait or landscape.
- The notification MPMoviePlayerContentPreloadDidFinishNotification has been deprecated. This notification was used in earlier versions to notify observers that a movie was loaded and ready to play.
The Movie Application
The application in this post is quite simple, it consists of a view controller with nothing more than a button to start playback and a second view controller to manage aMPMoviePlayerController and the NSURL of the movie. The two views are shown in the figures below:
Primary View Controller
The interface definition for the primary view is shown below:
#import <UIKit/UIKit.h>
 
@class CustomMoviePlayerViewController;
 
@interface TestViewController : UIViewController
{
CustomMoviePlayerViewController *moviePlayer;
UIButton *playButton;
}
 
@end
CustomMoviePlayerViewController is the controller for managing the movie, we’ll look at that code in a moment.
In the code below we create the view, add a play button, create a method for processing a button press event and within loadMoviePlayer, we get a reference to the movie file and create an instance of the CustomMoviePlayerViewController, which will load and play the movie.
- (void)loadView
{
// Setup the view
[self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]];
[[self view] setBackgroundColor:[UIColor grayColor]];
[[self view] setUserInteractionEnabled:YES];
 
// Add play button
playButton = [[UIButton alloc] initWithFrame:CGRectMake(53, 212, 214, 36)];
[playButton setBackgroundImage:[UIImage imageNamed:@"playButton.png"] forState:UIControlStateNormal];
[playButton addTarget:self action:@selector(buttonPressed:) forControlEvents: UIControlEventTouchUpInside];
[[self view] addSubview:playButton];
}
 
- (void)buttonPressed:(UIButton *)button
{
// If pressed, play movie
if (button == playButton)
[self loadMoviePlayer];
}
 
- (void)loadMoviePlayer
{
// Play movie from the bundle
NSString *path = [[NSBundle mainBundle] pathForResource:@"Movie-1" ofType:@"mp4" inDirectory:nil];
 
// Create custom movie player
moviePlayer = [[[CustomMoviePlayerViewController alloc] initWithPath:path] autorelease];
 
// Show the movie player as modal
[self presentModalViewController:moviePlayer animated:YES];
 
// Prep and play the movie
[moviePlayer readyPlayer];
}
Notice that the movie for this example is loaded from the application bundle. Also, once the custom movie player is created, the view is shown as a modal view.
Custom Movie Player View Controller
The interface definition for the movie view controller is below:
@interface CustomMoviePlayerViewController : UIViewController 
{
MPMoviePlayerController *mp;
NSURL *movieURL;
}
 
- (id)initWithPath:(NSString *)moviePath;
- (void)readyPlayer;
 
@end
The initialization code is where the movie player and associated URL are created. The primary goal of the initialization is to create the NSURL needed by the movie player.
- (id)initWithPath:(NSString *)moviePath
{
// Initialize and create movie URL
if (self = [super init])
{
movieURL = [NSURL fileURLWithPath:moviePath];
[movieURL retain];
}
return self;
}
The code to create the player and setup the notifications is where we start to deal with the differences in how the movie player works on various OS versions. Notice below the call torespondsToSelector, this is the Apple recommended way to check for feature availability, versus looking for a specific OS version.
For devices running 3.2 and above, the movie player controller has a method named loadstate, if this exists we can access a few additional methods to set the player to fullscreen as well as request the movie to begin preloading.
Equally important is the distinction of which notification to setup – see lines 16 and 24.
You can read more about managing different OS version in this post Developing iPhone Apps with iOS4 SDK, Deploying to 3.x Devices
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- (void) readyPlayer
{
mp = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
 
// For 3.2 devices and above
if ([mp respondsToSelector:@selector(loadState)])
{
// Set movie player layout
[mp setControlStyle:MPMovieControlStyleFullscreen];
[mp setFullscreen:YES];
 
// May help to reduce latency
[mp prepareToPlay];
 
// Register that the load state changed (movie is ready)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
else
{
// Register to receive a notification when the movie is in memory and ready to play.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePreloadDidFinish:)
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
}
 
// Register to receive a notification when the movie has finished playing.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
The next two chunks of code are for each of the selectors, one for each notification. The code below is for earlier OS versions – nothing more than removing the notification and asking the movie to play.
/*---------------------------------------------------------------------------
* For 3.1.x devices
* For 3.2 and 4.x see moviePlayerLoadStateChanged:
*--------------------------------------------------------------------------*/

- (void) moviePreloadDidFinish:(NSNotification*)notification
{
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerContentPreloadDidFinishNotification
object:nil];
 
// Play the movie
[mp play];
}
For the notification generated in 3.2 and above, there are a few more details to tend to:
/*---------------------------------------------------------------------------
* For 3.2 and 4.x devices
* For 3.1.x devices see moviePreloadDidFinish:
*--------------------------------------------------------------------------*/

- (void) moviePlayerLoadStateChanged:(NSNotification*)notification
{
// Unless state is unknown, start playback
if ([mp loadState] != MPMovieLoadStateUnknown)
{
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
 
// When tapping movie, status bar will appear, it shows up
// in portrait mode by default. Set orientation to landscape
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
 
// Rotate the view for landscape playback
[[self view] setBounds:CGRectMake(0, 0, 480, 320)];
[[self view] setCenter:CGPointMake(160, 240)];
[[self view] setTransform:CGAffineTransformMakeRotation(M_PI / 2)];
 
// Set frame of movie player
[[mp view] setFrame:CGRectMake(0, 0, 480, 320)];
 
// Add movie player as subview
[[self view] addSubview:[mp view]];
 
// Play the movie
[mp play];
}
}
Beyond removing the notification, we also adjust the status bar, rotate the view, set the frame of the movie player, add the movie player as a subview in the view controller and wrap it all up by asking the movie to play.
Note: MPMoviePlayerViewController is also an option over creating your own view controller as I’ve done here.
Source Code
The easiest way to see all this working is to download the source code and step through the code in the debugger.

Thursday, July 5, 2012

[J2ME] Send Text Message (Sms) using Wireless API

/* This example for sending sms using our SIM card.
Here im not using any PORT No to send the sms. so the received sms will be displayed in Inbox. This sms will be sent by using your sim card. so you will be charged for the same..*/

import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import javax.wireless.messaging.*;
public class SendSms extends MIDlet implements CommandListener {
      Display display;
      private TextField toWhom;
      private TextField message;
      private Alert alert;
      private Command send,exit;
      MessageConnection clientConn;
      private Form compose;

      public SendSms() {
            display=Display.getDisplay(this);
            compose=new Form("Compose Message");
            toWhom=new TextField("To","",10,TextField.PHONENUMBER);
            message=new TextField("Message","",600,TextField.ANY);
            send=new Command("Send",Command.BACK,0);
            exit=new Command("Exit",Command.SCREEN,5);
            compose.append(toWhom);
            compose.append(message);
            compose.addCommand(send);
            compose.addCommand(exit);
            compose.setCommandListener(this);
      }
      public void startApp() {
            display.setCurrent(compose);
      }
      public void pauseApp() {
      }
      public void destroyApp(boolean unconditional) {
            notifyDestroyed();
      }
      public void commandAction(Command cmd,Displayable disp) {
            if(cmd==exit) {
                  destroyApp(false);
            }
            if(cmd==send) {
                  String mno=toWhom.getString();
                  String msg=message.getString();
                  if(mno.equals("")) {
                        alert = new Alert("Alert");
                        alert.setString("Enter Mobile Number!!!");
                        alert.setTimeout(2000);
                        display.setCurrent(alert);
                  }
                  else {
                        try {
                              clientConn=(MessageConnection)Connector.open("sms://"+mno);
                        }
                        catch(Exception e) {
                              alert = new Alert("Alert");
                              alert.setString("Unable to connect to Station because of network problem");
                              alert.setTimeout(2000);
                              display.setCurrent(alert);
                        }
                        try {
                              TextMessage textmessage = (TextMessage) clientConn.newMessage(MessageConnection.TEXT_MESSAGE);
                              textmessage.setAddress("sms://"+mno);
                              textmessage.setPayloadText(msg);
                              clientConn.send(textmessage);
                        }
                        catch(Exception e)
                        {
                              Alert alert=new Alert("Alert","",null,AlertType.INFO);
                              alert.setTimeout(Alert.FOREVER);
                              alert.setString("Unable to send");
                              display.setCurrent(alert);
                        }
                  }
            }
      }
}

Wednesday, July 4, 2012

PushRegistry, tự khởi động ứng dụng…

PushRegistry là gì??
PushRegistry chỉ có trong MIDlet 2.0
PushRegistry được dùng để cho phép ứng dụng tự động khởi động khi có một tác nhân bên ngoài tác động vào. Có 2 loại tác nhân ở đây là
1 Connection được thiết lập(Ví dụ: SMS tới chẳng hạn), loại thứ 2 là hẹn giờ, ví dụ hẹn đến 2h30 thì tự động khởi động chẳng hạn.
Ở đây tôi ví dụ với tác nhân thứ hai, tác nhân thứ nhất bạn có thể tự tìm hiểu. Đây không phải là một chuyên mục lớn nên tôi gợi ý,
các bạn có thể tự khám phá. Sau đây là Code của 1 MIDlet đơn giản ứng dụng PushRegistry.

private void registerAlarm() {
final String className = this.getClass().getName();    //tên MIDlet được đăng ký
final Date d=new Date();    //dùng để xác định thời gian đăng ký
final Calendar cal = Calendar.getInstance();
cal.setTime(d);
//15h55
cal.set(Calendar.HOUR, 15);
cal.set(Calendar.MINUTE, 55);
//việc đăng ký cần được thực hiện trong 1 luồng riêng để tránh treo toàn bộ MIDlet khi chờ đăng ký
Thread t = new Thread(new Runnable(){ // tạm thời tôi viết đối tượng Runnable không tường minh
public void run() {
try {
//đăng ký để MIDlet được tự động nạp vào lúc 15h55
PushRegistry.registerAlarm(className, cal.getTime().getTime() );
} catch (ConnectionNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
t.start();
}
/* PushRegistry không nhất thiết là tự động khởi động một ứng dụng, nó có thể dùng trong 1 MIDlet để tự động khởi động 1 Class khác
trong File Jar.
Ví dụ như ta cần tự động đồng bộ dữ liệu giữa Mobile và máy chủ.
Do đó, ta sẽ tạo ra 1 Thread đồng bộ dữ liệu. Rồi trong MIDlet chính, ta sẽ khai báo 1 Timer với 1 TimerTask lặp lại quá trình
tự động khởi động Thread đó, dùng PushRegistry..
Tôi nghĩ để máy có thể tự động khởi động và làm việc tốt thì bạn nên đặt lệnh t.start() ở trong method exitApp(), và chạy ứng dụng
lần đầu cho Registry ghi nhận Permision.
*/

J2ME - SMS

SMS là một trong những phát minh vĩ đại của con người, tính chất đó không những đúng trước kia mà bây giờ cũng vậy. SMS vẫn luôn là một tính năng rất tuyệt vời của điện thoại di động. Bạn không thể nói từ trước đến giờ bạn chưa bao giờ sử dụng SMS, chính điều đó đã chứng tỏ điều tôi muốn nói với các bạn. Hôm nay tôi xin giới thiệu sơ lược các thao tác cơ bản với SMS trên thiết bị di động.
Để có thể gửi và nhận SMS, bạn cần có một kết nối SMS(MessageConnection).
Có 2 loại: SMS server và SMS client. Dùng để gửi và nhận tin SMS.
Nói chung SMS server hoàn toàn có các tính năng của SMS client. Do đó ta nên dùng SMS server.
Khởi tạo SMS server:
MessageConnection msgCnn = (MessageConnection) Connector.open(“sms://123456:0001″);
=> Khởi tạo SMS server với số 12345 là tùy chọn và số này không quan trọng lắm, trường hợp cần thiết tôi sẽ nói sau. Và cổng của SMS server là 0001.
Cổng của MessageConnection là một điều hết sức quan trọng. Nếu gửi tin đi mà không có cổng thì thì tin nhắn đó sẽ giống như các tin nhắn bình thường và sẽ được trình nhận tin nhắn mặc định của điện thoại nhận và lưu trong hộp thư đến của máy. Trong trường hợp ta gửi có cổng(port) riêng thì tin nhắn đó sẽ được chương trình của ta tiếp nhận, tất nhiên là chương trình của ta phải có kết nối vào đúng cổng đó. Nếu không cũng chẳng có ai nhận SMS đó cả.

Khi đã có MessageConnection ta sẽ thử gửi 1 tin nhắn:
TextMessage msg = (TextMessage) msgConn.newMessage(MessageConnection.TEXT_MESSAGE, “sms://” + number);
Trong đó String number sẽ là số mà ta gửi tin SMS đến. Tham số đầu tiên dùng để quy định dạng tin nhắn gì, có ba loại đó là TEXT_MESSAGE(tin nhắn chữ), BINARY_MESSAGE và MULTIPART_MESSAGE(tin đa phương tiện).
Tiếp theo ta tạo nội dung cho TextMessage trên:
msg.setPayloadText(content);
Trong đó String content là nội dung SMS mà ta muốn gửi.
Cuối cùng là thao tác gửi:
msgConn.send(msg);
Với hai loại tin nhắn còn lại các bạn tự khám phá cách cài đặt nội dung cho nó nhé.
Sau đây là SourceCode một App dùng Client Connection, nếu bạn muốn chuyển qua server Connection thì chỉ việc thêm Port ở sau, như thế này: addr = “sms://+8412345:0001″;
//////////////////
import javax.microedition.io.Connector;
import javax.microedition.midlet.*;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.MessageListener;
import javax.wireless.messaging.TextMessage;
public class SendSMS extends MIDlet implements MessageListener{
String addr;
String message=”This is my Messsage…”;
String number= “+84979163510″;
public void startApp() {
try {
//sets address to send message
addr = “sms://+8412345″;  // Số này không quan trọng(mấy số cũng được)…, tuy nhiên ko có thì ko được. Nghĩa là đang trong kết nối Client, chỉ gửi SMS thôi
// opens connection
MessageConnection conn = (MessageConnection)Connector.open(addr);
// prepares text message
TextMessage msg =(TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE,”sms://”+number);  // Số này mới quan trọng, đó sẽ là số mà ta gửi SMS đến
//set text
msg.setPayloadText(message);
// send message
conn.send(msg);
conn.close();
} catch (Exception se) {
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
public void notifyIncomingMessage(MessageConnection arg0) {
}
}
////////////////
Và đây là một ứng dụng SMS mà tôi đã viết, bạn có thể down vể và thử.