2015年11月12日 星期四

[iOS XMPP] 關於XMPP Framework的大小事 - xmpp stream設定

上一篇我們已經提到了XMPP Framework裡面主要分為兩個部分,xmpp core & xmpp extension。而xmpp core裡面最重要的就是xmpp stream,所以今天我們來看看如何在我們的app裡設定xmpp stream吧!
(在設定以下這些東西,通常都是在AppDelegate中進行實作)

1. 首先,設定連線

其實設定連線步驟很簡單,只要設定JID就可以了,範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"tungtungtsai@gmail.com"];
至於剩下的就交給xmpp stream完成吧!等等..我都還沒跟xmpp stream說我的server在哪啊?沒關係,在這裡,當你輸入了XMPPJID,xmpp stream就會去做SRV查找_xmpp-client._tcp.domain。例如,我在範例中打的domain是gmail.com,google server就很可能回傳像是"talk.google.com"的東西,然後xmpp stream就可以去和這個server建立連線。當然,如果說SRV查找失敗的話,那麼xmpp stream就直接去連JID的domain了。所以你要是已經知道你準備要連過去的xmpp server並沒有xmpp SRV records,你可以直接告訴xmpp stream你的host name,讓他不用花時間去查找了(反正也找不到...),範例如下:
xmppStream.myJID = [XMPPJID jidWithString:@"user@tungtungtsai.com"];
xmppStream.hostName = @"tungtungtsai.com";
你直接指派hostname有個最大好處就是他在development xmpp server也可以work。例如你的伺服器沒有DNS address只有實體IP,或是只能在local network run,那也沒關係,一樣能夠work。範例如下:

xmppStream.myJID = [XMPPJID jidWithString:@"user@dev1.myCompany.com"];
xmppStream.hostName = @"192.168.2.27";
設定了這些之後還有一件事,就是port。我們可以選擇性的看要不要給port,對每個xmpp spec來說,預設xmpp server都是開port 5222,當然如果你要開不同port就要特別設定一下hostPost了。


2. 添加代理(Delegates)

上一篇提到關於XMPPStream有些讓framework更具彈性、延展性、易開發的設計,而其一就是MulticastDelegate。由於xmpp framework需要支援很多的extensions,包含官方的和個人開發者所自己撰寫的plugin,所以傳統的代理模式(Delegate pattern)並不適合。因此,MulticastDelegate就可以讓你在你自己的extension中使用標準的delegate paradigm,並且允許多個classes去接收同一個delegate notification。這樣地好處是你不需要把你所有的xmpp handling code放進單一個class。

這裡簡單提一下:

在iOS開發中,主要有兩個callback systems,delegate和notification。Delegate很直覺也很簡單。使用者把自己設為delegate,然後就可以實作自己需要的delegate method。例如:

[worker setDelegate:self];

- (void)workerDidFinish:(Worker *)sender
{
}

- (void)worker:(Worker *)sender didFinishSubTask:(id)subtask inDuration:(NSTimeInterval)elapsed
{
}

- (BOOL)worker:(Worker *)sender shouldPerformSubTask:(id)subtask
{
}
Notifications也不難,使用者需要個別地註冊每個notification type (寫起來複雜一些),例如:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinish:)
                                             name:WorkerDidFinishNotification
                                           object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(workerDidFinishSubTask:)
                                             name:WorkerDidFinishSubTaskNotification
                                           object:nil];

- (void)workerDidFinish:(NSNotification *)notification
{
    Worker *sender = [notification object];
}

- (void)workerDidFinishSubTask:(NSNotification *)notification
{
    Worker *sender = [notification object];
    id subtask = [[notification userInfo] objectForKey:@"subtask"];
    NSTimeInterval elapsed = [[[notification userInfo] objectForKey"duration"] doubleValue];
}

有時候要從notification中的Dictionary取出參數的話,就要先知道他擁有哪些keys。另外,notification是不允許回傳變數的。因此簡單地對兩個callback機制的優缺點分析如下:

Delegate優點:

1. 易於註冊多個callback
2. 當有多個參數時較易於使用
3. 允許回傳變數

Delegate缺點:

1. 只能夠是單一delegate

Notification優點:

1. 可以多個物件註冊同一個notification

Notification缺點:

1. 註冊多個callback較複雜
2. 要從dictionary中拿到參數很麻煩(要知道正確的key)
3. 當需要回傳變數時就不能使用

所以,對於XMPP framework來說哪一個方式適用呢?對XMPP來說,xmpp framework需要能夠廣播給多個listeners。例如當你有個message進來,你需要同時廣播給聊天的視窗、歷史紀錄和推播系統等等。此外,xmpp framework需要易於延展。xmpp要能夠支持很多的XEP,所以要能在broadcast端與listener端易於使用。xmpp framework 需要選擇能支持回傳變數的方式,也需要能夠幫助維持 thread-safety,因為xmpp framework大量地使用平行化與多線程。

講完你就發現好像delegate和notification都不是最好的解決方法,因此xmpp就創了一個GCDMulticastDelegate class。你可以在任何時候把你自己新增/移除為XMPPStream的delegate。

[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
...
[xmppStream removeDelegate:self];
簡單範例如下:
// Add myself as a delegate, and tell xmppStream to invoke my delegate methods on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// Then just implement whatever delegate methods you need like normal
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
   ...
}
就和傳統的代理模式差不多,但是你可以具體地說明thread-specific資訊。而如果你想要讓某些process離開主線程到背景執行也很容易,在iPhone中這種操作方式可以讓你的app維持良好的perfomance,範例如下
// Handle most stuff on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];

// But do that one slow thing on a background queue so it doesn't slow down the UI anymore
[xmppStream addDelegate:bgProcessor delegateQueue:bgProcessorQueue];


3. 添加模組(Modules)

接下來就是要加入我們自己需要的extensions了,當然你也可以自己寫自己需要的extensions,這裡我們不會探討到底有哪些extensions,僅簡單列出一些,因為這裡最重要的是要學會加入模組。
XMPPReconnect - 當斷線時自動重新連線
XMPPRoster - 提供聯絡人功能
XMPPRoom - 提供聊天室,群聊功能
XMPPPubSub - 提供發布與訂閱功能
現在我們來試著加入XMPPReconnect module到我們的xmpp stream吧!

xmppReconnect = [ [XMPPReconnect alloc] init];

// Optional configuration of xmppReconnect could go here.
// The defaults are fine for our purposes.

[xmppReconnect activate:xmppStream];

// You can also optionally add delegates to the module.

[xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()];

// And that's all that is needed.
// The module will receive any delegate methods it needs automatically
// from the xmpp stream, and will continue to do its thing unless you deactivate it.

4. 建立連線

當我們都加好要用的modules,就可以開始建立連線了!

NSError *error = nil;
if (![xmppStream connect:&error])
{
    NSLog(@"Oops, I probably forgot something: %@", error);
}

若是我們在過程中忘記設定必須的屬性,例如JID,那連線就會回傳NO,並在error message中給予提示。

在連線過程中,client端和server端會建立xmpp handshake(交握),達成協定。如果你的伺服器需要建立安全連線,例如透過SSL/TLS方式,xmpp stream會自動建立安全連線。如果你的伺服器有不正確的X509 certificate,那就必須去實作xmppStream:willSecureWithSettings: 這個方法來改掉預設的安全設定。

5. 認證(Authenticating)

在所有連線handshaking都已經完成後,就會開始呼叫 xmppStreamDidConnect:方法,並開始進行認證程序。程式碼如下:

- (void)xmppStreamDidConnect:(XMPPStream *)sender
{
    [xmppStream authenticateWithPassword:password error:NULL];
}


五個步驟進行完,在實際執行時就能與xmpp stream進行連線操作嘍!


參考連結:https://github.com/robbiehanson/XMPPFramework/wiki/IntroToFramework

[iOS XMPP] 關於XMPP Framework的大小事 - xmpp core 簡介

最近又要開始寫chat application。其中我們要用來做聊天的protocol是:XMPP。因此簡單地把最近一些我對XMPP Framework的了解寫下來,希望這些筆記能帶給大家之後再開發過程更加清楚也更有方向。

XMPP全名是Extensible Messaging and Presence Protocol,他是一個protocol,他的用途並不局限於chat而已,還有很多其他的應用層面,不過我們這裡依舊專注於討論與chat有關的事情。

這裡所討論的是XMPP framework,這個framework是以objective-c為語言,主要分成兩個部分:xmpp core 跟 xmpp extension。xmpp core是核心的部分,主要在實現xmpp的SPEC (RFC 3920)。除了core之外,很多其他的開發者會有各自的需求,因此這些開發者就開始撰寫一些extension,讓xmpp能有更多客製化的功能,例如:自動重新連線、聯絡人、使用者狀態等等,這些xmpp extension可以在官方文件裡找到,通常都已XEP開頭,詳情參閱這裡

在xmpp core裡面,主要包含了這些:


  • XMPPStream
  • XMPPParser
  • XMPPJID
  • XMPPElement
  • XMPPIQ
  • XMPPMessage
  • XMPPPresence
  • XMPPModule
  • XMPPLogging
  • XMPPInternal
以下逐一簡單介紹一下。

XMPPStream 是這個framework的核心,之後是我們最主要進行互動的一個class,而這個class也是所有extension和客製化程式碼的主要接口。他提供很多功能讓framework更具彈性、延展性並讓其他開發者容易開發。


XMPPParser就是...Parser...反正他...就是parser...我們也不太會跟這個class接觸就是了


XMPPJID這個主要是在實作JID(Jabber Identifier),什麼是JID呢?每個user都會有他獨立的JID,可以簡單假想成一種在XMPP中的個人ID嘍。他與NSCopying protocol一致,所以我們可以把JID用做是NSDictionary裡的key。


XMPPElement是三個主要的XMPP元素中的base class: XMPPIQ, XMPPMessage, XMPPPresense。這個class是NSXMLElement的延伸,所以我們可以透過這個去看到任何的xml元素。在XMPP中的傳輸資訊也都是使用XML在傳輸。此外,在framework裡有一個 NSXMLElement+XMPP category,讓我們的程式碼可以更簡單易讀。


XMPPModule主要是提供一個class可以選擇性地去加入一些extensions。這裡比較重要的是:如果你是一個想要開發標準的XEP,或是你希望你自己寫的extension是可以pluggable,那你就必須要架構在XMPPModule之上了。


XMPPLogging提供一個良好的log framework,讓你可以輕鬆把一些app的狀態記錄下來。


XMPPInternal就是一些比較內部的東西,在開發上接觸的機會很低,就不花時間研究了。


介紹完xmpp core中的這些class,下一篇就可以來看看如何設定XMPPStream嘍!

參考連結:https://github.com/robbiehanson/XMPPFramework/wiki/IntroToFramework

2015年11月4日 星期三

[iOS] Action Extension 簡易教學

這幾天在研究iOS裡面的Extension。中文資源好像不太多,所以我把一些最近看到的心得陸續寫一下。

今天我要做的簡單教學,是讓APP可以打開Action Sheet,裡面會有一個Read按鈕,當使用者點了Read,APP就會幫你念出APP中的文字內容。




其實Extension最常被使用的地方是:雖然你沒有開啟APP,但你可以讓使用者可以使用某些APP功能或內容,所以通常使用Extension的時候都是因為有些特殊工作需要執行。Extension的種類很多,今天我要介紹的是Action Extension,這個Extension主要的目的是讓使用者查看或操作你APP裡的內容。在實作Extension特別需要注意的一個觀念是:Extension並不是一個APP唷!

好了,我們開始吧!


Step 1. 打開Xcode 新增一個Project,使用Single View Application並取一個自己喜歡的Project名字,我這裡取的是:“ReadExtPractice”





Step 2. 從Target的地方新增一個Target,並選擇Action Extension,一樣自己取一個你喜歡的名字吧!這裡我是叫做"Read"。然後在Activate處先選Cancel吧。






Step 3. 現在就開始來動工做一下簡單的UI吧!這裡我們以iPhone作為例子,就暫且不考慮那些iphone尺寸不同問題拉。先到ReadActionExt裡面Main.storyboard裡點選View Controller,並把 Simulated Metrics裡面的size設定成 iPhone 4.7-inch,就假設他是iPhone6吧。然後加入一個TextView與一個Button,並且把TextView的內文設定為自己想要的內容,如果不知道寫什麼,就跟我寫一樣吧!我的是“Hello, my name is Tung-tung Tsai. Welcome to Use Action Extension. Have a nice day.”並將按鈕的Title換成"Action"。如果不知道元件的size如何設定比較好,可以參考我這裡是設定textview{0, 28, 375, 200},button{112, 250, 150, 40}。






Step 4. 接下來開始寫點code拉。先到ViewController.h裡加入textView這個property並命名為"textView"並加入按鈕的Action命名為"ActionPressed",然後到ViewController.m裡面找到- (IBAction)ActionPressed:(id)sender 這個函數,並在裡面加上這段code,讓使用者點擊按鈕後出現一個Activity Controller。

- (IBAction)ActionPressed:(id)sender {
    UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:@[self.textView.text] applicationActivities:nil];
    [self presentViewController:activityVC animated:YES completion:nil];
}





Step 5.  都沒問題後就要開始來處理Action Extension的部分拉!一樣先處理UI,我們到Read裡面打開MainInterface.storyboard,這裡原本會有一個imageView不過我們不需要他,就直接將他Delete吧,然後加上一個TextView,並將Navigation Bar的標題改成"Read Ext"。

然後進入ActionViewController.h裡加入一個property命名為"TextView"連結到剛剛你加到storyboard裡的TextView吧!





Step 6.  打開ActionViewController.m,先在上面@import AVFoundation後,將- (void)viewDidLoad 的code先拿掉並重新寫上:

    NSExtensionItem *item = self.extensionContext.inputItems[0];
    NSItemProvider *itemProvider = item.attachments[0];
    
    if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePlainText]) {

        [itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypePlainText options:nil completionHandler:^(NSString* item, NSError * error) {
            if (item) {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    [self.TextView setText:item];
                    
                    //Set Synthesizer and utterence
                    AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc]init];
                    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:self.TextView.text];
                    [utterance setRate:0.5];
                    [synthesizer speakUtterance:utterance];
                }];
            }
        }];
        
    }



Step 7. 完成上述程式碼就代表他會在Extension被呼叫時讀取原本在TextView裡面的文字並且朗讀出來。但還沒結束,還需要在Read裡面的Info.plist設定一些參數才行。先進入Info.plist,然後打開NSExtension -> NSExtensionAttributes -> NSExtensionActivationRule,將NSExtensionActivationRule的type改為Dictionary,並加入一個新的項目NSExtensionActivationSupportsText,把這個type設為Boolean,並把Value設為YES。



Step 8. 終於完成了,最後到Asset.xcassets裡面把120x120的圖示放進去,並在App icon source裡面設定Icon來源為AppIcon就完成了。







Step 9. 實際執行看看吧!!應該會出現下面的樣子,如果我點選Action,再點選Read就會出現一個新頁面然後開始讀文字內容給你聽嘍!!!!











完成了! 希望這個教學可以幫助你更認識Action Extension嘍!如果有需要這份代碼,可以從Github上面查看。


[iOS] 透過UIWebview內嵌Html5 video影片到APP時常見問題

最近在著手研究關於iOS內嵌影片的問題,最簡單的方式是在設計APP時把影片包進APP裡,但是這樣的方式雖然簡單卻不見得符合需求,因為這樣操作等APP上架後就無法隨時更新,給使用者看新影片了!

因此我們接下來要討論的就是希望能把放在網路上的影片拿到手機上來播放。最基本的方式當然是先從網路上把整個影片下載下來,然後再拿播放器放映。不過這方法如果是一些應付大檔案就麻煩了,光是要等待影片下載好的時間可能就會讓使用者受不了。於是第二個好方法就是透過串流方式,不過要串流需要streaming server的配合,並不好說做就做。因此,最近我試著使用webview內嵌html5 video來進行播放。這樣的好處自然是使用者不用下載就可以觀看影片,同時,他不用straming server也有類似streaming的效果可以邊播邊載,他的載影片方式是一次一個request去load一部分的影片,不一個request就把所有的影片載完,同時他也可以做seek,就是讓使用者直接點擊影片時間條,點擊到哪裡就播哪裡。

關於html5 video內嵌,可以從Apple的文件上取得一些相關資訊,以下我就幾個我自己在過程中遇到且很常會有人想問的問題做簡單說明。

Q1. 在iPhone內嵌html5 video是否一定要全螢幕才可以播放?

Ans. 是的。官方文件說要播影片就是要全屏,我試了一陣子真的沒辦法,只要要播就會跳到全屏。但是iPad的話就不需要,可以不用全屏就能播影片。

Q2. 可以讓影片自動進行播放嗎?

Ans. 可以。在html端要設定control。

<video controls = "controls" autoplay ="autoplay">

在app端你的webview要設定以下屬性:

self.WebView.allowsInlineMediaPlayback = YES; // for autoplay
self.WebView.mediaPlaybackRequiresUserAction = NO; //for autoplay


Q3. 如何透過objective-C內嵌html5 video,代碼來一下?

self.WebView = [[UIWebView allocinit];
 self.WebView.scalesPageToFit = YES;
 self.WebView.allowsInlineMediaPlayback = NO; // for autoplay
 self.WebView.mediaPlaybackRequiresUserAction = YES; //for autoplay
        
 NSString *serverUrl = [NSString stringWithFormat:@"http://%@:%@@domain.com/video.mp4"username,password];
 serverUrl = [serverUrl stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        
 NSURL *url = [NSURL URLWithString:serverUrl];
 NSURLRequest *req = [NSURLRequest requestWithURL:url];
 [self.WebView loadRequest:req];
 [self.view addSubviewself.VideoWebView];

如果是iPad,可以設定video的frame來fit你的webview,因此你可以透過loadHTMLString來做。
 NSString *serverUrl = [NSString stringWithFormat:@"http://%@:%@@domain.com/video.mp4"username,password];
 serverUrl = [serverUrl stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *videoHTML= [NSString stringWithFormat@"<style type='text/css'>body { margin:0;background-color:black}</style><video width='%f' height='%f' frameborder='0' controls autoplay><source src=\"%@\" type=\"video/mp4\"></video><meta name='viewport' content='width=device-width, initial-scale=1,user-scalable=no,minimum-scale=1.0, maximum-scale=1.0'>"self.VideoWebView.frame.size.widthself.VideoWebView.frame.size.height,serverUrl];
[self.VideoWebView loadHTMLString:videoHTML baseURL:nil];
 [self.view addSubviewself.VideoWebView];



Q4. 網址一旦使用https:// 影片就無法播了拉!!!怎麼這樣?

Ans. 因為你的server需要先裝憑證。

Q5. 我已經在app登入過拉,為什麼拿不到伺服器的影片,哭哭,怎麼辦?

Ans. 因為在web裡面要用basic authentication才行,他沒有幫你記住...所以你要把原本的連結由:"http://domain.com/video/sample.mp4"改為在前面加上帳號密碼,來通過認證,變這樣:
"http://username:password@domain.com/video/sample.mp4" 那你會問我這樣不就被人家發現帳號密碼...對啊,但這是資安問題了QQ

Q6. 現在我已經會透過webview來播影片了,但是我想要從特定時間點開始播可以嗎?例如直接從十秒開始播!

Ans. 可以的,請參考以下代碼。把<script>寫進去,然後currenttime設定一下,設定幾秒就會從幾秒開始播嘍!他的方式是從開始就從10秒處打request,所以前面1~9秒就不會loading了!

 NSString *videoHTML= [NSString stringWithFormat: @"<style type='text/css'>body { margin:0;background-color:black}</style><video id='video1' width='%f' height='%f' frameborder='0' controls autoplay><source src=\"%@\" type=\"video/mp4\"></video><script>document.getElementById('video1').addEventListener('loadedmetadata', function() {this.currentTime = 10;}, false);</script><meta name='viewport' content='width=device-width, initial-scale=1,user-scalable=no,minimum-scale=1.0, maximum-scale=1.0'>"self.WebView.frame.size.width, self.WebView.frame.size.height,serverUrl];


Q7.  我有辦法知道他正在看哪裡(第幾秒)嗎?

Ans. 應該是不行,因為我試了好一陣子,我也想知道答案,誰知道快教教我XDD

2015年11月1日 星期日

objective-C : add a html5 video in iOS app


If we don't have a streaming server and wanna let our video "looks like" we have a steaming effect in our app, such as seek or preview, we can use "html5 video" as a solution.We can check this documents offered by Apple.


When I used UIWebview to include HTML5 video in our app, I got some questions. I tried to find answers and here's what I found and a lot of people often ask.

Q1. Can I use html5 video "without fullscreen" in iPhone?

Ans.  No. If you want to play the video, it must be played in fullscreen. I also have tried some ways, but failed. You can check the document provided by Apple. They don't allow us to play html5 video without fullscreen. But in iPad, you can play the video without fullscreen. According to the document, this is for better user experience...

Q2. Can I use autoplay?

Ans. Yes. You need to add control in your html code,

<video controls = "controls" autoplay ="autoplay">

 and set your webview like this :

self.WebView.allowsInlineMediaPlayback = YES; // for autoplay
self.WebView.mediaPlaybackRequiresUserAction = NO; //for autoplay


Q3. How could I load a HTML5 Video in objective-c code?

Ans.

self.WebView = [[UIWebView allocinit];
 self.WebView.scalesPageToFit = YES;
 self.WebView.allowsInlineMediaPlayback = NO; // for autoplay
 self.WebView.mediaPlaybackRequiresUserAction = YES; //for autoplay
        
 NSString *serverUrl = [NSString stringWithFormat:@"http://%@:%@@domain.com/video.mp4"username,password];
 serverUrl = [serverUrl stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        
 NSURL *url = [NSURL URLWithString:serverUrl];
 NSURLRequest *req = [NSURLRequest requestWithURL:url];
 [self.WebView loadRequest:req];
 [self.view addSubviewself.VideoWebView];


if you want to set video's frame,  you can use this code (let webview load HTMLString):


 NSString *serverUrl = [NSString stringWithFormat:@"http://%@:%@@domain.com/video.mp4"username,password];
 serverUrl = [serverUrl stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *videoHTML= [NSString stringWithFormat@"<style type='text/css'>body { margin:0;background-color:black}</style><video width='%f' height='%f' frameborder='0' controls autoplay><source src=\"%@\" type=\"video/mp4\"></video><meta name='viewport' content='width=device-width, initial-scale=1,user-scalable=no,minimum-scale=1.0, maximum-scale=1.0'>"self.VideoWebView.frame.size.widthself.VideoWebView.frame.size.height,serverUrl];
[self.VideoWebView loadHTMLString:videoHTML baseURL:nil];
 [self.view addSubviewself.VideoWebView];

Q4. My url is https, and I cannot play the video, why?

Ans. If you try to link the url with https, your server needs certificate.

Q5. I have logged in to my server, but I still cannot get the video by url, why?

Ans. Maybe  you need to use basic authentication to connect with server, which means you need to use "http://username:password@domain.com/video/sample.mp4" replace the origin url "http://domain.com/video/sample.mp4"

Q6. I want to let video play from certain time, not from 0, can I do that?

Ans. Yes. You need to add js to your videoHTML. For example, you want to play at 10s, you can do this : set the current time and you can set the begin time.

 NSString *videoHTML= [NSString stringWithFormat: @"<style type='text/css'>body { margin:0;background-color:black}</style><video id='video1' width='%f' height='%f' frameborder='0' controls autoplay><source src=\"%@\" type=\"video/mp4\"></video><script>document.getElementById('video1').addEventListener('loadedmetadata', function() {this.currentTime = 10;}, false);</script><meta name='viewport' content='width=device-width, initial-scale=1,user-scalable=no,minimum-scale=1.0, maximum-scale=1.0'>"self.WebView.frame.size.width, self.WebView.frame.size.height,serverUrl];


Q7. Can I know what time user is watching now? (Can I get the current time in APP?)

Ans. I think we cannot get the current time in app but I cannot sure that. If you can, please let me know!!!