Creating a Voice Recognition Game with AS3? - actionscript-3

I have been surfing the Internet searching for a solution for offline speech recognition in Air for android and ios and have just come across this code.
I'm wondering how can I modify this code to create an application that is actually a 'Voice Recognition Game'.
I should Define/add a "sentence" to each frame and check if users voice input matches that "sentence" or not...
For example we add this sentence to frame 10 : "This is a hello world test." if timeline reaches frame 10 then :
1) Speech recognition automatically starts.
2) User's voice convert to text.
3) If user's input matches "This is a hello world test." then we continue to frame 11
4) if user's input does not match, then speech recognition starts again (To let the user his second try..his last try)
5) This time if user's input match our sentence, continue to frame 11 and if it doesn't match go to frame 1
I don't need any text to speech that is included in the code or any of controllers and I just want English as my speech to text engine.any help or suggestions is greatly appreciated.
here is the link to the code and source files :
https://fabricemontfort.com/voice-recognition-and-speech-synthesis-in-as3-with-air/
And here is the code :
<?xml version="1.0" encoding="utf-8"?>
<f:Application xmlns:f="library://ns.feathersui.com/mxml"
xmlns:fx="http://ns.adobe.com/mxml/2009"
theme="feathers.themes.MetalWorksMobileTheme">
<fx:Script>
<![CDATA[
import feathers.controls.Alert;
import starling.events.Event;
/**
* STT and TTS AIR Native Extensions
*/
import com.fabricemontfort.air.ezSTT;
import com.fabricemontfort.air.ezSpeech;
import com.fabricemontfort.air.ezspeech.languages;
import com.fabricemontfort.air.ezstt.STTEvent;
import com.fabricemontfort.air.ezstt.languages;
/**
* Text To Speech engine
*/
private var tts:ezSpeech = ezSpeech.instance;
/**
* Speech To Text engine
*/
private var stt:ezSTT = ezSTT.instance;
/**
* actionButton Labels
*/
private static const OUCH:String = "NOT SUPPORTED";
private static const AUTH:String = "ASK PERMISSIONS";
private static const START:String = "START LISTENING";
private static const STOP:String = "STOP LISTENING";
/**
* STT/TTS utterance
*/
private var _utterance:String = "";
[Bindable]
public function get utterance():String {
return _utterance;
}
public function set utterance(value:String):void {
_utterance = value;
resultLabel.text = _utterance;
}
/**
* STT Microphone volume
*/
private var _volume:Number = 0;
[Bindable]
public function get volume():Number {
return _volume;
}
public function set volume(value:Number):void {
_volume = value;
}
/**
* Initialize TTS speed
*/
private var _speed:Number = 0.48;
[Bindable]
public function get speed():Number {
return _speed;
}
public function set speed(value:Number):void {
_speed = value;
tts.setSpeed(speed);
}
/**
* Initialize TTS pitch
*/
private var _pitch:Number = 0.65;
[Bindable]
public function get pitch():Number {
return _pitch;
}
public function set pitch(value:Number):void {
_pitch = value;
tts.setPitch(pitch);
}
override protected function initialize():void {
super.initialize();
// Hide debug messages for ezSTT and ezSpeech
stt.debug = false;
tts.debug = false;
// Set TTS voice speed
tts.setSpeed(speed);
// Set TTS voice pitch
tts.setPitch(pitch);
// Set default language for engines
stt.setLanguage(com.fabricemontfort.air.ezstt.languages.EN);
tts.setLanguage(com.fabricemontfort.air.ezspeech.languages.US);
// Check if STT is supported
if (stt.isSupported()) {
// Check if STT is authorized
if (stt.isAuthorized()) {
// Add STT listeners for final result, partial result, volume, end of speech
stt.addEventListener(STTEvent.PARTIAL, onSTTResult);
stt.addEventListener(STTEvent.FINAL, onSTTResult);
stt.addEventListener(STTEvent.VOL, onSTTVolume);
stt.addEventListener(STTEvent.STOP, onSTTStop);
// Everything is fine, lets start
actionButton.label = START;
// STT is not autorized
} else {
// Initialize STT listener for permissions
stt.addEventListener(STTEvent.AUTH, onAuth);
actionButton.label = AUTH;
}
// STT is not supported
} else {
actionButton.label = OUCH;
var alert:Alert = Alert.show("STT is not supported", "Error", new ListCollection(
[
{label: "OK"}
]));
}
}
/**
* STT engine recognized words
*/
private function onSTTResult(event:STTEvent):void {
// Set utterance with partial and final result
utterance = event.message;
}
/**
* The microphone volume changed
*/
private function onSTTVolume(event:STTEvent):void {
// Set STT microphone volume
volume = parseInt(event.message);
}
/**
* User stopped speaking or clicked the stop button
*/
private function onSTTStop(event:STTEvent):void {
actionButton.label = START;
volume = 0;
// Wait 1 second before repeating last utterance
setTimeout(repeatUtterance, 1000);
}
private function onAuth(event:STTEvent):void {
// Check if STT is authorized
if (stt.isAuthorized()) {
// Remove this listener
stt.removeEventListener(STTEvent.AUTH, onAuth);
// Add STT listeners for final result, partial result, volume, end of speech
stt.addEventListener(STTEvent.PARTIAL, onSTTResult);
stt.addEventListener(STTEvent.FINAL, onSTTResult);
stt.addEventListener(STTEvent.VOL, onSTTVolume);
stt.addEventListener(STTEvent.STOP, onSTTStop);
// Everything is fine, lets start
actionButton.label = START;
// STT is not autorized
} else {
// Show an error message
var alert:Alert = Alert.show("Please give permissions and retry", "Error", new ListCollection(
[
{label: "OK"}
]));
}
}
private function actionButton_triggeredHandler(event:Event):void {
// Check actionButton label
switch (actionButton.label) {
// Ask for permissions
case AUTH: {
stt.askUserAuthorization();
break;
}
// Start STT engine and change label
case START: {
stt.start();
actionButton.label = STOP;
break;
}
// Stop STT engine and change label
case STOP: {
stt.stop();
actionButton.label = START;
break;
}
}
}
/**
* User changed the current language
*/
private function langPickerList_changeHandler(event:Event):void {
// Set current language for STT and TTS engines
stt.setLanguage(langPickerList.selectedItem.code);
tts.setLanguage(langPickerList.selectedItem.codeSpeech);
}
/**
* User asked to enforce a specific language with country code
*/
private function forceLangTextInput_changeHandler(event:Event):void {
// Try to enfore current language for STT and TTS engines (experimental)
stt.forceLanguage(forceLangTextInput.text);
tts.forceLanguage(forceLangTextInput.text);
}
/**
* User asked to repeat the utterance
*/
private function sayButton_triggeredHandler(event:starling.events.Event):void {
repeatUtterance();
}
private function repeatUtterance():void {
// if utterence is not empty
if (utterance != "" && utterance != "[]") {
// and tts is supported
if (tts.isSupported()) {
// Let's talk
tts.say(utterance);
// tts is not supported
} else {
// show a error message
var alert:Alert = Alert.show("TTS is not supported", "Error", new ListCollection(
[
{label: "OK"}
]));
}
}
}
/**
* User changed the speed Slider
*/
private function speedSlider_changeHandler(event:Event):void {
speed = speedSlider.value;
}
/**
* User changed the pitch Slider
*/
private function pitchSlider_changeHandler(event:starling.events.Event):void {
pitch = pitchSlider.value;
}
]]>
</fx:Script>
<!-- This is a vertical layout with padding and gapping of 10 -->
<f:layout>
<f:VerticalLayout gap="10"
padding="10"/>
</f:layout>
<fx:Declarations>
<!-- Fit the target component to the maximum available space -->
<f:VerticalLayoutData id="fitScreen"
percentWidth="100"
percentHeight="100"/>
<!-- Fit the target component to me maximum width -->
<f:VerticalLayoutData id="fitWidth"
percentWidth="100"/>
<!-- List of all supported languages -->
<f:ListCollection id="langListCollection">
<fx:Object label="English"
code="{com.fabricemontfort.air.ezstt.languages.EN}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.US}"/>
<fx:Object label="French"
code="{com.fabricemontfort.air.ezstt.languages.FR}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.FR}"/>
<fx:Object label="German"
code="{com.fabricemontfort.air.ezstt.languages.DE}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.DE}"/>
<fx:Object label="Italian"
code="{com.fabricemontfort.air.ezstt.languages.IT}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.IT}"/>
<fx:Object label="Spanish"
code="{com.fabricemontfort.air.ezstt.languages.ES}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.ES}"/>
<fx:Object label="Chinese"
code="{com.fabricemontfort.air.ezstt.languages.ZH}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.ZH}"/>
<fx:Object label="Japanese"
code="{com.fabricemontfort.air.ezstt.languages.JA}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.JA}"/>
<fx:Object label="Russian"
code="{com.fabricemontfort.air.ezstt.languages.RU}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.RU}"/>
<fx:Object label="Korean"
code="{com.fabricemontfort.air.ezstt.languages.KO}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.KO}"/>
<fx:Object label="Portuguese"
code="{com.fabricemontfort.air.ezstt.languages.PT}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.PT}"/>
<fx:Object label="Czech"
code="{com.fabricemontfort.air.ezstt.languages.CS}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.CS}"/>
<fx:Object label="Dutch"
code="{com.fabricemontfort.air.ezstt.languages.NL}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.NL}"/>
<fx:Object label="Polish"
code="{com.fabricemontfort.air.ezstt.languages.PL}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.PL}"/>
<fx:Object label="Swedish"
code="{com.fabricemontfort.air.ezstt.languages.SV}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.SV}"/>
<fx:Object label="Turkish"
code="{com.fabricemontfort.air.ezstt.languages.TR}"
codeSpeech="{com.fabricemontfort.air.ezspeech.languages.TR}"/>
</f:ListCollection>
</fx:Declarations>
<f:Panel id="resultPanel"
title="RESULTS"
layoutData="{fitScreen}">
<f:layout>
<f:VerticalLayout padding="10"/>
</f:layout>
<!-- This is where user can read the utterance -->
<f:Label id="resultLabel"
wordWrap="true"
text="{utterance}"
layoutData="{fitScreen}"/>
</f:Panel>
<f:Label text="TTS VOICE SPEED"
layoutData="{fitWidth}"/>
<!-- This is where the user can change the TTS voice speed -->
<f:Slider id="speedSlider"
change="speedSlider_changeHandler(event)"
layoutData="{fitWidth}"
minimum="0"
maximum="1"
step="0.05"
value="{speed}"/>
<f:Label text="TTS VOICE PITCH"
layoutData="{fitWidth}"/>
<!-- This is where the user can change the TTS voice pitch -->
<f:Slider id="pitchSlider"
change="pitchSlider_changeHandler(event)"
layoutData="{fitWidth}"
minimum="0.5"
maximum="1.5"
step="0.05"
value="{pitch}"/>
<f:Label text="STT MICROPHONE VOLUME"/>
<!-- This is where the user can see the STT microphone volume -->
<f:ProgressBar id="volumeBar"
minimum="0"
maximum="12"
value="{volume}"
layoutData="{fitWidth}"/>
<!-- This is where the user can pick a language -->
<f:PickerList id="langPickerList"
change="langPickerList_changeHandler(event)"
focusPadding="20"
dataProvider="{langListCollection}"
layoutData="{fitWidth}"/>
<f:Label id="forceLangLabel"
text="TEST A COUNTRY CODE (EXPERIMENTAL)"
layoutData="{fitWidth}"/>
<!-- This is where the user can enforce a language with country code -->
<f:TextInput id="forceLangTextInput"
padding="20"
change="forceLangTextInput_changeHandler(event)"
layoutData="{fitWidth}"/>
<!-- This is where the user have to click to give permissions, start speaking, stop speaking -->
<f:Button id="actionButton"
padding="20"
triggered="actionButton_triggeredHandler(event)"
layoutData="{fitWidth}"/>
<!-- This is where the user have to click to repeat the utterance -->
<f:Button id="sayButton"
padding="20"
label="REPEAT THE LAST UTTERANCE"
triggered="sayButton_triggeredHandler(event)"
layoutData="{fitWidth}"/>
</f:Application>

First of all thank you for your confidence on ezSTT and ezSpeech.
As indicated by Organis. The source code provided above is oriented AS3 (and Feathers MXML). It is not really thought to use the internal mechanics of Animate CC.
But nothing is lost so far. If you want to use the notions of timeline, frames, scenes, it is quite possible for you to mix both approaches.
Keep the pure AS3 base for recognition and speech synthesis, as well as for sentence checking and routing to animations.
Use the frames inside a MovieClip to show awesome anims to the player and make the connection between the two with events.
This will allow you to keep an "animation" approach while keeping the source code as simple as possible to maintain.
I hope this will help you. Happy coding with AS3.

Related

Flex Background Worker disposing of failed MessageChannel freezes application

I have the following scenario: I am using a MessageChannel to communicate with a background worker in a flex application. Since the code is to eventually be transformed into a library, it should also be able to handle malformed input (e.g. sending classes via the channel for which no class alias is registered in the background worker). In this case I want to abort the worker. My code is the following:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
public class BW extends Sprite
{
/** Incoming channel */
private var fromCoordinatorChannel:MessageChannel;
/** Outgoing channel */
private var toCoordinatorChannel:MessageChannel;
public function BW()
{
super();
initChannels();
}
/**
* Get channels from shared property and attach event listener
*/
private function initChannels():void {
// Get channnels from shared property
fromCoordinatorChannel = Worker.current.getSharedProperty("toWorkerChannel");
toCoordinatorChannel = Worker.current.getSharedProperty("fromWorkerChannel");
// Attach event listener for incoming messages
fromCoordinatorChannel.addEventListener(Event.CHANNEL_MESSAGE, onIncomingMessage);
}
/**
* Event handler for incoming messages on the channel.
* #param event Event that came in
*/
private function onIncomingMessage(event:Event):void {
handleIncoming();
}
/**
* Get oldest message from channel and handle it
*/
private function handleIncoming():void {
if(fromCoordinatorChannel.messageAvailable) {
try {
var wm:Object = fromCoordinatorChannel.receive(true);
} catch(e:Error) {
fromCoordinatorChannel.close();
trace("Invalid type of package sent - could not be deserialized.");
// Kill myself
fromCoordinatorChannel = null;
toCoordinatorChannel = null;
Worker.current.setSharedProperty("toWorkerChannel", null);
Worker.current.setSharedProperty("fromWorkerChannel", null);
Worker.current.terminate();
}
}
}
}
}
And in the primordial worker:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
var worker:Worker;
var to:MessageChannel;
var from:MessageChannel;
var graveyard:Array = new Array();
private function removeWorkerIfFailed():void {
if(worker && worker.state == WorkerState.TERMINATED) {
from.close();
worker = null;
// What the actual f***? If I allow this channel to be garbage collected, it breaks. If I prevent that, it doesn't (o.Ó)
graveyard.push(to);
to = null;
from = null;
}
}
protected function button1_clickHandler(event:MouseEvent):void
{
registerClassAlias("Example", Example);
// Create worker and channels
worker = WorkerDomain.current.createWorker(Workers.BW);
to = Worker.current.createMessageChannel(worker);
from = worker.createMessageChannel(Worker.current);
// Attach event listener to status of worker so its reference can be deleted when it fails
worker.addEventListener(Event.WORKER_STATE,function(event:Event):void {removeWorkerIfFailed();});
// Set shared properties so worker can access channels
worker.setSharedProperty("toWorkerChannel", to);
worker.setSharedProperty("fromWorkerChannel", from);
// Attach event listener for incoming messages
from.addEventListener(Event.CHANNEL_MESSAGE, function(event:Event):void { trace('incoming'); });
// Start the worker
worker.start();
var example1:Example = new Example("one");
to.send(example1);
}
]]>
</fx:Script>
<s:Button label="Do it" click="button1_clickHandler(event)">
</s:Button>
</s:WindowedApplication>
Add the Example class
package
{
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;
public class Example implements IExternalizable
{
public var name:String;
public function Example(name:String)
{
this.name = name;
}
public function readExternal(input:IDataInput):void
{
name = input.readUTF();
}
public function writeExternal(output:IDataOutput):void
{
output.writeUTF(name);
}
}
}
The problem is the following: If I remove the line in the removeWorkerIfFailed() that pushes a reference to the array (thereby preventing the channel from being garbage collected), the main application freezes. The debugger does not show any active function calls. As long as that line is there, everything works fine.
To reiterate: I know that in order to fix it, I need to call the registerClassAlias(...) also in the background worker, but I am trying to handle precisely this case that someone throws something wrong at the background worker.
Thank you

DataGrid ItemRenderer

Here's the thing: I've this DataGrid that holds a column with the purpose of handle some actions. So, this DataGrid have several data columns and, at the end there is this special column.
At this special column named "operations" there are two icons; one of 'em shows an alarm icon (like a little bell). So, what I've to accomplish is that, initially the alarm icon shows invisible; when the user sets an alarm (through another interface) the alarm icon shows up (with default style color) and when it's time to fire the alarm the alarm icon is supposed to take another style color (like yellow).
So, I've this next definition:
<mx:DataGrid id="dgSomeValues"
dragEnabled="true"
draggableColumns="false"
width="100%" height="100%"
horizontalScrollPolicy="off"
resizableColumns="true"
rowHeight="19">
<components:columns>
<mx:DataGridColumn id="dgcItem" headerText="{resourceManager.getString('resources','columnItem')}" width="70" resizable="false"/>
<!--
Some other dataGridColumns
-->
<mx:DataGridColumn id="dgcOperation" headerText=" " width="50" resizable="false">
<mx:itemRenderer>
<fx:Component>
<components:OperationItemRenderer/>
</fx:Component>
</mx:itemRenderer>
</mx:DataGridColumn>
</components:columns>
And definition for OperationItemRenderer is like this:
import flash.events.MouseEvent;
import com.broker.control.BrokerEvent;
import mx.containers.HBox;
public class OperationItemRenderer extends HBox
{
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* Alarm Button --> bell
**/
private var btnAlarm:Icon;
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor
**/
public function OperationItemRenderer()
{
super();
this.setStyle("horizontalGap",0);
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/**
* #inheritDoc
**/
override public function set data(value:Object):void
{
super.data = value;
}
/**
* #inheritDoc
**/
protected override function createChildren():void
{
super.createChildren();
if (!btnAlarm){
btnAlarm = new Icon();
btnAlarm.styleName = ""; // Initially do not shows any icon.
btnAlarm.enabled = true;
btnAlarm.addEventListener(MouseEvent.CLICK, btnAlarmClickHandler,false,0,true);
addChild(btnAlarma);
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (btnAlarm){
btnAlarm.width = unscaledWidth/2;
btnAlarm.height= unscaledHeight;
}
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* If this item has an alarm, then it will show an icon.
* States for alarm icon are: Default --> icnAlarmOff
* Fired --> icnAlarm
*/
public function upgradeIcon(toogle:Boolean):void
{
btnAlarm.styleName = toogle ? "icnAlarm" : "icnAlarmOff";
}
//--------------------------------------------------------------------------
//
// Event Handlers
//
//--------------------------------------------------------------------------
protected function btnAlarmaClickHandler(event:MouseEvent):void
{
if (btnAlarm.styleName == "favIcnAlarma") {
var evt:BrokerEvent;
evt = new BrokerEvent(BrokerEvent.SETUP_ALARM);
dispatchEvent(evt);
}
}
}
The function "upgradeIcon" it's supposed to be called from another section in the application where a rowFunction it's called everytime the DataGrid's datasource is refreshed. At this rowFunction I want to find a way to access DataGrid ItemRenderer so that way I'll be able to call this upgradeIcon function.
The question is, how can I access DataGrid's ItemRenderer programatically? What I already tried is something like this:
var c:ClassFactory = view.dgcOperation.itemRenderer as ClassFactory;
if (c != null && c.generator != null) { // c never is null at this point
var f:OperationItemRenderer = c.generator as OperationItemRenderer;
f.upgradeIcon(change); // f it's always null
//(c.generator as OperationItemRenderer).upgradeIcon(change);
}
But this approach does not work. Any help would be greatfully appreciated.
Thanks in advance!
try to dispatch an event when user set alarm...and add a listener of this event in your renderer, so when a new alarm is set, an event will be dispatched from your alarm interface, and be captured in your renderer which will update your icon style.
Your datagrid must be having a data provider of value objects (say Report is name of your class) . If you have marked your Report class bindable then any change in any of its property will dispatch PropertyChangeEvent and then you can override set Data method in the item-renderer and based on the value of that property you should have taken necessary action.
ItemRenderers are recycled and fetching them in this was is a bad practice. You should not alter an ItemRenderer directly rather alter the underlying object.

Android HTML5 video fullscreen and rotation

i read the article
Android WebView: handling orientation changes
and follow the tip i build a project
MainActivity
package com.example.testvideo1;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.webkit.WebView;
import android.os.Build;
#SuppressLint("NewApi")
public class MainActivity extends ActionBarActivity {
private VideoEnabledWebView webView;
private VideoEnabledWebChromeClient webChromeClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set layout
setContentView(R.layout.activity_main);
// Save the web view
webView = (VideoEnabledWebView) findViewById(R.id.webView);
// Initialize the VideoEnabledWebChromeClient and set event handlers
View nonVideoLayout = findViewById(R.id.nonVideoLayout);
// Your own view,
//Your own view,
ViewGroup videoLayout = (ViewGroup) findViewById(R.id.videoLayout);
View loadingView = null; // Your own view, read class comments
webChromeClient = new VideoEnabledWebChromeClient(nonVideoLayout,
videoLayout, loadingView, webView) // See all available
// constructors...
{
// Subscribe to standard events, such as onProgressChanged()...
#Override
public void onProgressChanged(WebView view, int progress) {
// Your code...
}
};
webChromeClient
.setOnToggledFullscreen(new VideoEnabledWebChromeClient.ToggledFullscreenCallback() {
#Override
public void toggledFullscreen(boolean fullscreen) {
// Your code to handle the full-screen change, for
// example showing and hiding the title bar. Example:
if (fullscreen) {
WindowManager.LayoutParams attrs = getWindow()
.getAttributes();
attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setAttributes(attrs);
if (android.os.Build.VERSION.SDK_INT >= 14) {
getWindow()
.getDecorView()
.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
} else {
WindowManager.LayoutParams attrs = getWindow()
.getAttributes();
attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
attrs.flags &= ~WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
getWindow().setAttributes(attrs);
if (android.os.Build.VERSION.SDK_INT >= 14) {
getWindow().getDecorView()
.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_VISIBLE);
}
}
}
});
webView.setWebChromeClient(webChromeClient);
// Navigate everywhere you want, this classes have only been tested on
// YouTube's mobile site
webView.loadUrl("http://app.vlooks.cn/webchat/html5/2300/home");
}
#Override
public void onBackPressed() {
// Notify the VideoEnabledWebChromeClient, and handle it ourselves if it
// doesn't handle it
if (!webChromeClient.onBackPressed()) {
if (webView.canGoBack()) {
webView.goBack();
} else {
// Close app (presumably)
super.onBackPressed();
}
}
}
}
follow is VideoEnabledWebChromeClient.java
package com.example.testvideo1;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.webkit.WebChromeClient;
import android.widget.FrameLayout;
/**
* This class serves as a WebChromeClient to be set to a WebView, allowing it to
* play video. Video will play differently depending on target API level
* (in-line, fullscreen, or both).
*
* It has been tested with the following video classes: -
* android.widget.VideoView (typically API level <11) -
* android.webkit.HTML5VideoFullScreen$VideoSurfaceView/VideoTextureView
* (typically API level 11-18) -
* com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView
* (typically API level 19+)
*
* Important notes: - For API level 11+, android:hardwareAccelerated="true" must
* be set in the application manifest. - The invoking activity must call
* VideoEnabledWebChromeClient's onBackPressed() inside of its own
* onBackPressed(). - Tested in Android API levels 8-19. Only tested on
* http://m.youtube.com.
*
* #author Cristian Perez (http://cpr.name)
*
*/
public class VideoEnabledWebChromeClient extends WebChromeClient implements
OnPreparedListener, OnCompletionListener, OnErrorListener {
public interface ToggledFullscreenCallback {
public void toggledFullscreen(boolean fullscreen);
}
private View activityNonVideoView;
private ViewGroup activityVideoView;
private View loadingView;
private VideoEnabledWebView webView;
private boolean isVideoFullscreen; // Indicates if the video is being
// displayed using a custom view
// (typically full-screen)
private FrameLayout videoViewContainer;
private CustomViewCallback videoViewCallback;
private ToggledFullscreenCallback toggledFullscreenCallback;
/**
* Never use this constructor alone. This constructor allows this class to
* be defined as an inline inner class in which the user can override
* methods
*/
#SuppressWarnings("unused")
public VideoEnabledWebChromeClient() {
}
/**
* Builds a video enabled WebChromeClient.
*
* #param activityNonVideoView
* A View in the activity's layout that contains every other view
* that should be hidden when the video goes full-screen.
* #param activityVideoView
* A ViewGroup in the activity's layout that will display the
* video. Typically you would like this to fill the whole layout.
*/
#SuppressWarnings("unused")
public VideoEnabledWebChromeClient(View activityNonVideoView,
ViewGroup activityVideoView) {
this.activityNonVideoView = activityNonVideoView;
this.activityVideoView = activityVideoView;
this.loadingView = null;
this.webView = null;
this.isVideoFullscreen = false;
}
/**
* Builds a video enabled WebChromeClient.
*
* #param activityNonVideoView
* A View in the activity's layout that contains every other view
* that should be hidden when the video goes full-screen.
* #param activityVideoView
* A ViewGroup in the activity's layout that will display the
* video. Typically you would like this to fill the whole layout.
* #param loadingView
* A View to be shown while the video is loading (typically only
* used in API level <11). Must be already inflated and without a
* parent view.
*/
#SuppressWarnings("unused")
public VideoEnabledWebChromeClient(View activityNonVideoView,
ViewGroup activityVideoView, View loadingView) {
this.activityNonVideoView = activityNonVideoView;
this.activityVideoView = activityVideoView;
this.loadingView = loadingView;
this.webView = null;
this.isVideoFullscreen = false;
}
/**
* Builds a video enabled WebChromeClient.
*
* #param activityNonVideoView
* A View in the activity's layout that contains every other view
* that should be hidden when the video goes full-screen.
* #param activityVideoView
* A ViewGroup in the activity's layout that will display the
* video. Typically you would like this to fill the whole layout.
* #param loadingView
* A View to be shown while the video is loading (typically only
* used in API level <11). Must be already inflated and without a
* parent view.
* #param webView
* The owner VideoEnabledWebView. Passing it will enable the
* VideoEnabledWebChromeClient to detect the HTML5 video ended
* event and exit full-screen. Note: The web page must only
* contain one video tag in order for the HTML5 video ended event
* to work. This could be improved if needed (see Javascript
* code).
*/
public VideoEnabledWebChromeClient(View activityNonVideoView,
ViewGroup activityVideoView, View loadingView,
VideoEnabledWebView webView) {
this.activityNonVideoView = activityNonVideoView;
this.activityVideoView = activityVideoView;
this.loadingView = loadingView;
this.webView = webView;
this.isVideoFullscreen = false;
}
/**
* Indicates if the video is being displayed using a custom view (typically
* full-screen)
*
* #return true it the video is being displayed using a custom view
* (typically full-screen)
*/
public boolean isVideoFullscreen() {
return isVideoFullscreen;
}
/**
* Set a callback that will be fired when the video starts or finishes
* displaying using a custom view (typically full-screen)
*
* #param callback
* A VideoEnabledWebChromeClient.ToggledFullscreenCallback
* callback
*/
public void setOnToggledFullscreen(ToggledFullscreenCallback callback) {
this.toggledFullscreenCallback = callback;
}
#Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (view instanceof FrameLayout) {
// A video wants to be shown
FrameLayout frameLayout = (FrameLayout) view;
View focusedChild = frameLayout.getFocusedChild();
// Save video related variables
this.isVideoFullscreen = true;
this.videoViewContainer = frameLayout;
this.videoViewCallback = callback;
// Hide the non-video view, add the video view, and show it
activityNonVideoView.setVisibility(View.INVISIBLE);
activityVideoView.addView(videoViewContainer, new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
activityVideoView.setVisibility(View.VISIBLE);
if (focusedChild instanceof android.widget.VideoView) {
// android.widget.VideoView (typically API level <11)
android.widget.VideoView videoView = (android.widget.VideoView) focusedChild;
// Handle all the required events
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);
videoView.setOnErrorListener(this);
} else {
// Other classes, including:
// - android.webkit.HTML5VideoFullScreen$VideoSurfaceView, which
// inherits from android.view.SurfaceView (typically API level
// 11-18)
// - android.webkit.HTML5VideoFullScreen$VideoTextureView, which
// inherits from android.view.TextureView (typically API level
// 11-18)
// -
// com.android.org.chromium.content.browser.ContentVideoView$VideoSurfaceView,
// which inherits from android.view.SurfaceView (typically API
// level 19+)
// Handle HTML5 video ended event only if the class is a
// SurfaceView
// Test case: TextureView of Sony Xperia T API level 16 doesn't
// work fullscreen when loading the javascript below
if (webView != null
&& webView.getSettings().getJavaScriptEnabled()
&& focusedChild instanceof SurfaceView) {
// Run javascript code that detects the video end and
// notifies the Javascript interface
String js = "javascript:";
js += "var _ytrp_html5_video_last;";
js += "var _ytrp_html5_video = document.getElementsByTagName('video')[0];";
js += "if (_ytrp_html5_video != undefined && _ytrp_html5_video != _ytrp_html5_video_last) {";
{
js += "_ytrp_html5_video_last = _ytrp_html5_video;";
js += "function _ytrp_html5_video_ended() {";
{
js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must
// match
// Javascript
// interface
// name
// and
// method
// of
// VideoEnableWebView
}
js += "}";
js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
}
js += "}";
webView.loadUrl(js);
}
}
// Notify full-screen change
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(true);
}
}
}
#Override
#SuppressWarnings("deprecation")
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) // Available in API level 14+,
// deprecated in API level 18+
{
onShowCustomView(view, callback);
}
#Override
public void onHideCustomView() {
// This method should be manually called on video end in all cases
// because it's not always called automatically.
// This method must be manually called on back key press (from this
// class' onBackPressed() method).
if (isVideoFullscreen) {
// Hide the video view, remove it, and show the non-video view
activityVideoView.setVisibility(View.INVISIBLE);
activityVideoView.removeView(videoViewContainer);
activityNonVideoView.setVisibility(View.VISIBLE);
// Call back (only in API level <19, because in API level 19+ with
// chromium webview it crashes)
if (videoViewCallback != null
&& !videoViewCallback.getClass().getName()
.contains(".chromium.")) {
videoViewCallback.onCustomViewHidden();
}
// Reset video related variables
isVideoFullscreen = false;
videoViewContainer = null;
videoViewCallback = null;
// Notify full-screen change
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(false);
}
}
}
#Override
public View getVideoLoadingProgressView() // Video will start loading, only
// called in the case of
// VideoView (typically API
// level 10-)
{
if (loadingView == null) {
return super.getVideoLoadingProgressView();
} else {
loadingView.setVisibility(View.VISIBLE);
return loadingView;
}
}
#Override
public void onPrepared(MediaPlayer mp) // Video will start playing, only
// called in the case of
// android.widget.VideoView
// (typically API level <11)
{
if (loadingView != null) {
loadingView.setVisibility(View.GONE);
}
}
#Override
public void onCompletion(MediaPlayer mp) // Video finished playing, only
// called in the case of
// android.widget.VideoView
// (typically API level <11)
{
onHideCustomView();
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) // Error while
// playing
// video, only
// called in the
// case of
// android.widget.VideoView
// (typically
// API level
// <11)
{
return false; // By returning false, onCompletion() will be called
}
/**
* Notifies the class that the back key has been pressed by the user. This
* must be called from the Activity's onBackPressed(), and if it returns
* false, the activity itself should handle it. Otherwise don't do anything.
*
* #return Returns true if the event was handled, and false if was not
* (video view is not visible)
*/
public boolean onBackPressed() {
if (isVideoFullscreen) {
onHideCustomView();
return true;
} else {
return false;
}
}
}
and VideoEnabledWebView.java
package com.example.testvideo1;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import java.util.Map;
/**
* This class serves as a WebView to be used in conjunction with a
* VideoEnabledWebChromeClient. It makes possible: - To detect the HTML5 video
* ended event so that the VideoEnabledWebChromeClient can exit full-screen.
*
* Important notes: - Javascript is enabled by default and must not be disabled
* with getSettings().setJavaScriptEnabled(false). - setWebChromeClient() must
* be called before any loadData(), loadDataWithBaseURL() or loadUrl() method.
*
* #author Cristian Perez (http://cpr.name)
*
*/
public class VideoEnabledWebView extends WebView {
public class JavascriptInterface {
#android.webkit.JavascriptInterface
public void notifyVideoEnd() // Must match Javascript interface method
// of VideoEnabledWebChromeClient
{
// This code is not executed in the UI thread, so we must force that
// to happen
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
if (videoEnabledWebChromeClient != null) {
videoEnabledWebChromeClient.onHideCustomView();
}
}
});
}
}
private VideoEnabledWebChromeClient videoEnabledWebChromeClient;
private boolean addedJavascriptInterface;
public VideoEnabledWebView(Context context) {
super(context);
addedJavascriptInterface = false;
}
#SuppressWarnings("unused")
public VideoEnabledWebView(Context context, AttributeSet attrs) {
super(context, attrs);
addedJavascriptInterface = false;
}
#SuppressWarnings("unused")
public VideoEnabledWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
addedJavascriptInterface = false;
}
/**
* Indicates if the video is being displayed using a custom view (typically
* full-screen)
*
* #return true it the video is being displayed using a custom view
* (typically full-screen)
*/
public boolean isVideoFullscreen() {
return videoEnabledWebChromeClient != null
&& videoEnabledWebChromeClient.isVideoFullscreen();
}
/**
* Pass only a VideoEnabledWebChromeClient instance.
*/
#Override
#SuppressLint("SetJavaScriptEnabled")
public void setWebChromeClient(WebChromeClient client) {
getSettings().setJavaScriptEnabled(true);
if (client instanceof VideoEnabledWebChromeClient) {
this.videoEnabledWebChromeClient = (VideoEnabledWebChromeClient) client;
}
super.setWebChromeClient(client);
}
#Override
public void loadData(String data, String mimeType, String encoding) {
addJavascriptInterface();
super.loadData(data, mimeType, encoding);
}
#Override
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
addJavascriptInterface();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
#Override
public void loadUrl(String url) {
addJavascriptInterface();
super.loadUrl(url);
}
#Override
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
addJavascriptInterface();
super.loadUrl(url, additionalHttpHeaders);
}
private void addJavascriptInterface() {
if (!addedJavascriptInterface) {
// Add javascript interface to be called when the video ends (must
// be done before page load)
addJavascriptInterface(new JavascriptInterface(),
"_VideoEnabledWebView"); // Must match Javascript interface
// name of
// VideoEnabledWebChromeClient
addedJavascriptInterface = true;
}
}
}
and the layout file
<!-- View that will be hidden when video goes fullscreen -->
<RelativeLayout
android:id="#+id/nonVideoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.VideoEnabledWebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- View where the video will be shown when video goes fullscreen -->
<RelativeLayout
android:id="#+id/videoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- View that will be shown while the fullscreen video loads (maybe include a spinner and a "Loading..." message) -->
<View
android:id="#+id/videoLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible" />
</RelativeLayout>
the project can't work, and i want when fullscreen , let the program go LandScape mode.
but i failed.
thanks.......
I've been facing the same issue.
Although this question is quite old, I have come to a simple solution which I could not find on stack overflow.
Just enforce Landscape when going full screen:
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
And revert this, when going back.
In context:
public class BaseWebChromeClient extends WebChromeClient
{
#Override
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback)
{
[...]
act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
[...]
#Override
public void onHideCustomView(View view, WebChromeClient.CustomViewCallback callback)
{
[...]
act.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// use SCREEN_ORIENTATION_SENSOR, if you don't to enforce portrait mode.
[...]
}
}

How to make simple actionscript itemrenderer for Flex Mobile?

I've been making mxml itemRenderers, but from what I hear at adobe, for the Flex Mobile projects they keep saying "make your item renderers with only actionscript3, no mxml"
So...I have this list where im trying to remake the itemRenderer in actionscript the best way I can guess to do so. can some one let me know if im doing something wrong? Maybe I should be doing it in a whole different file..i dont know this is my first time making an all actionscript3 IR.
The text appears, but now my scollToBottom() function no longer works now. I used it with my mxml itemrenderer and it worked fine. so i thought maybe I was doing something wrong here...So this is my primary problem, im assuming something is wrong with how im doing the itemrenderer and thats why the scroll to bottom function wont work anymore.
//my scroll to bottom function that i run after i put something in the list. since its a chat, this way it auto scrolls down for the user to read the latest message.
protected function scrollToBottom():void {
// update the verticalScrollPosition to the end of the List
// virtual layout may require us to validate a few times
var delta:Number = 0;
var count:int = 0;
while (count++ < 10){
chat_list.validateNow();
delta = chat_list.layout.getVerticalScrollPositionDelta(NavigationUnit.END);
chat_list.layout.verticalScrollPosition += delta;
if (delta == 0)
break;
}
}
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" width="100%" height="100%" autoDrawBackground="false" contentBackgroundAlpha=".3" creationComplete="itemrenderer1_creationCompleteHandler(event)">
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#font-face {
src: url("assets/fonts/mpb.ttf");
fontFamily: "myFont";
embedAsCFF: true;
advancedAntiAliasing: true;
}
</fx:Style>
<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import spark.components.Label;
import spark.components.VGroup;
private var msgTxt:Label = new Label();
private var nameLabel:Label = new Label();
private var mainContainer:VGroup = new VGroup();
protected function itemrenderer1_creationCompleteHandler(event:FlexEvent):void
{
maxWidth=this.width;
mainContainer.paddingBottom=10;
mainContainer.paddingTop=10;
mainContainer.verticalAlign="bottom";
mainContainer.explicitWidth=this.width;
this.addElement(mainContainer);
msgTxt.setStyle("fontFamily","myFont");
msgTxt.setStyle("color","#000000");
msgTxt.setStyle("fontSize","35");
msgTxt.setStyle("paddingRight","15");
msgTxt.setStyle("paddingTop","10");
msgTxt.setStyle("paddingLeft","15");
msgTxt.explicitWidth=this.width;
mainContainer.addElement(msgTxt);
nameLabel.setStyle("fontFamily","myFont");
nameLabel.setStyle("color","#666666");
nameLabel.setStyle("paddingLeft","5");
nameLabel.setStyle("fontSize","24");
nameLabel.explicitWidth=this.width;
mainContainer.addElement(nameLabel);
}
override public function set data(value:Object):void {
super.data = value;
if (data == null)
return;
if(data.systemMsg)
{
}
if(data.name)
{
nameLabel.text=data.name;
if(data.name == "You: ")
{
nameLabel.setStyle("textAlign","right");
msgTxt.setStyle("textAlign","right");
nameLabel.setStyle("paddingRight","5");
}
else if(data.name == "Them: ")
{
nameLabel.setStyle("textAlign","left");
msgTxt.setStyle("textAlign","left");
}
else
{
nameLabel.setStyle("textAlign","left");
msgTxt.setStyle("textAlign","left");
}
}
if(data.icon)
{
}
if(data.msg)
{
msgTxt.text=data.msg;
}
}
]]>
</fx:Script>
</s:ItemRenderer>
what you are missing are a few function calls that need to be overwritten so that the size and position of your items are correctly measured at the right point in the work flow. Here is a copy/paste of the code from the default Flex Template.
Also, from the look of things is looks like you are trying to put as3 code into a Flex ItemRenderer, but that isn't going to help you performance wise. You are going to need a pure AS3 class that extends a Class such as LabelItemRenderer
/**
* #private
*
* Override this setter to respond to data changes
*/
override public function set data(value:Object):void
{
super.data = value;
// the data has changed. push these changes down in to the
// subcomponents here
}
/**
* #private
*
* Override this method to create children for your item renderer
*/
override protected function createChildren():void
{
super.createChildren();
// create any additional children for your item renderer here
}
/**
* #private
*
* Override this method to change how the item renderer
* sizes itself. For performance reasons, do not call
* super.measure() unless you need to.
*/
override protected function measure():void
{
super.measure();
// measure all the subcomponents here and set measuredWidth, measuredHeight,
// measuredMinWidth, and measuredMinHeight
}
/**
* #private
*
* Override this method to change how the background is drawn for
* item renderer. For performance reasons, do not call
* super.drawBackground() if you do not need to.
*/
override protected function drawBackground(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.drawBackground(unscaledWidth, unscaledHeight);
// do any drawing for the background of the item renderer here
}
/**
* #private
*
* Override this method to change how the background is drawn for this
* item renderer. For performance reasons, do not call
* super.layoutContents() if you do not need to.
*/
override protected function layoutContents(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.layoutContents(unscaledWidth, unscaledHeight);
// layout all the subcomponents here
}

AS3 for Facebook - why is my login callback not being called?

We're developing a Flash site that integrates with Facebook heavily.
We've noticed a bug where, if a user is not logged into Facebook, and we try to log them in via Facebook.login in the AS3 classes provided by Adobe, the callback is invoked, but the parameters result:Object and fail:Object are BOTH null. If our user then tries to login again, it all works fine, returning the userid in result:Object if successful.
Is this a bug with Facebook itself? Has anyone seen this before? We're sure this wasn't happening about a week ago.
Facebook.login(onLogin, {perms:Config.FB_PERMISSIONS});
private function onLogin(result:Object, fail:Object):void {
// first time this is called after the popup closes returns
// result == null and fail == null
// second time around, results are as expected
}
Cheers,
Mark.
this is a way to connect to Facebook using Graph API. Everything is explained in comment. This is actually the connecting to facebook, no posting to walls or anything. That part can be found below this class.
package com.DAL
{
import com.facebook.graph.Facebook;
import flash.events.Event;
import com.facebook.graph.data.FacebookSession;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import com.fbLoginButton;
import com.adobe.serialization.json.JSON;
public class FBConnect extends EventDispatcher
{
/******************************************
* Variables
******************************************/
private var _applicationID:String;
private var _extendedPermissions:Object;
/******************************************
* Constants
******************************************/
public static const CONNECTED:String = "CONNECTED";
/******************************************
* Properties
******************************************/
public function get applicationID():String
{
return _applicationID;
}
/******************************************
* Constructor
******************************************/
public function FBConnect()
{
super();
//Set applicationid
_applicationID = "YOUR_ID";
//Set permissions to ask for
_extendedPermissions = {perms:"read_stream, publish_stream, user_about_me, read_friendlists, user_photos"};
//Initialize facebook
Facebook.init(_applicationID);
}
/******************************************
* Methods
******************************************/
public function login(e:MouseEvent):void
{
Facebook.login(handleLogin, _extendedPermissions);
}
private function handleLogin(response:Object, fail:Object):void
{
dispatchEvent(new Event(CONNECTED));
}
}
}
That should take care of connecting to facebook. If you want to post to walls or anything, you can find a small example below.
/******************************************
* Constructor
******************************************/
public function FBLogic()
{
super();
_connect = new FBConnect();
_connect.addEventListener(FBConnect.CONNECTED, startLoaders);
initLoaders();
}
/******************************************
* Methods
******************************************/
...
public function post(message:String):void
{
var _params:Object = new Object();
_params.access_token = Facebook.getSession().accessToken;
_params.message = message;
Facebook.api("/" + _userID + "/feed", messagePosted, _params, "POST");
}
public function messagePosted(response:Object, fail:Object):void
{
dispatchEvent(new Event(MESSAGEPOSTED));
}
public function login(e:MouseEvent):void
{
var _loginButton:fbLoginButton = e.target as fbLoginButton;
_loginButton.alpha = 0;
_loginButton.visible = false;
_connect.login(e);
}
If this doesn't do the trick you might have forgotten to add some code to your html file. Be sure to add the following code to the head of your html file:
<script type="text/javascript" src="http://connect.facebook.net/en_US/all.js"></script>
And you also need a div called fb-root, declared like this.
<body>
<div id="fb-root"></div>
<div id="flashContent">
</div>
</body>
This is kinda dirty, but for me a timeout before calling login worked fine:
setTimeout(Facebook.login, 200, onLogin, {perms:Config.FB_PERMISSIONS});
_extendedPermissions = {perms:"read_stream, publish_stream, user_about_me, read_friendlists, user_photos"};
Blockquote
"perms" is now "scope"
{scope:"read_stream, publish_stream, user_about_me, read_friendlists, user_photos"};
This is not very well documented on the Adobe Facebook API page.
SHORT ANSWER
Just keep Facebook.init() looping every 0.5 seconds until it finds a valid credential cookie.
LONG ANSWER
The docs describe Facebook.init() as a setup function. But it does more than that, it looks for valid fb logon cookie. As does Facebook.login().
The idea is call Facebook.init() at startup and see if user already have access. Else use Facebook.login() to make the user logon.
Not executable code:
/**
* Start the app, and see if there are any valid credentials
**/
public function startApp() {
Facebook.init(fbAppid, handleFbLogin);
}
/**
* This is a method to call the app didnt login successfully
* at startup.
**/
private function tryLogin() {
Facebook.login(handleFbLogin, {});
}
private var lastLoginSuccess:Object; //being a bit pedantic maybe
/**
* This is the callback that is <i>supposed</i> to run
* ones the user has logged in.
* Unfortunately the callback assoc. with the login/init
* goes off almost instantly; and so often fails before
* valid credentials have been creations >:(
**/
public function handleFbLogin(success:Object, fail:Object) {
lastLoginSuccess = success;
if(success){
//yay!
} else {
//taps foot impatiently...
flash.utils.setTimeout(tryFbInitIfLastFailed, 500);
}
}
/**
* And also the user could be taking their sweet time
* typing in their password.
* Oh and giving their device informative names like 'qwe'
*
* So when they finally login, and the regularly executing
* handleFbLogin() will finally find some valid credentials
**/
private function tryFbInitIfLastFailed() {
if (lastLoginSuccess == null)
Facebook.init(view.fbApp.id, handleFbLogin);
}
Any why not keep calling Facebook.login()? because the opens the Facebook login popup - so you will have a steady stream of popups.
This works for me, either if im not logged in or if im already:
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
Facebook.init("APPID", onInit);
}
private function onInit(response:Object, fail:Object):void
{
if(response)
{
handleLoginResponse(true, null);
}
else
{
Facebook.login(handleLoginResponse, { scope: "email, user_photos, publish_stream" } );
}
}
private function handleLoginResponse(result:Object, fail:Object):void
{
if (result != null) {
// Logged in
} else {
// Goodbye :(
}
}
Add https for all.js in the tag you use in the html file