//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package spark.preloaders { import flash.display.DisplayObject; import flash.display.Sprite; import flash.display.StageAspectRatio; import flash.display.StageOrientation; import flash.events.Event; import flash.events.StageOrientationEvent; import flash.geom.Matrix; import flash.system.Capabilities; import flash.utils.getTimer; import mx.core.RuntimeDPIProvider; import mx.core.mx_internal; import mx.events.FlexEvent; import mx.managers.SystemManager; import mx.preloaders.IPreloaderDisplay; import mx.preloaders.Preloader; use namespace mx_internal; /** * The SplashScreen class is the default preloader for Mobile Flex applications. * * Developers can specify image class and resize mode through the Application properties * splashScreenImage, splashScreenScaleMode and * splashScreenMinimumDisplayTime. * * The SplashScreen monitors device orientation and updates the image so that it * appears on screen as if the orientation is always StageOrientation.DEFAULT. * * @see spark.components.Application#splashScreenImage * @see spark.components.Application#splashScreenScaleMode * @see spark.components.Application#splashScreenMinimumDisplayTime * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public class SplashScreen extends Sprite implements IPreloaderDisplay { //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function SplashScreen() { super(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * The splash image */ private var splashImage:DisplayObject; // The splash image private var splashImageWidth:Number; // original pre-transform width private var splashImageHeight:Number; // original pre-transform height private var SplashImageClass:Class; // The class of the generated splash image private var dynamicSourceAttempted:Boolean = false; // Have we tried to create the dynamicSource instance? private var dynamicSource:SplashScreenImage; // Instance of the SplashScreenImage sub-class if one is passed in // as the value of Application's splashScreenImage property. /** * @private * The resize mode for the splash image */ private var info:Object = null; // The systemManager's info object private var scaleMode:String = "none"; // One of "none", "stretch", "letterbox" and "zoom". /** * @private * Minimum time for the image to be visible */ private var minimumDisplayTime:Number = 1000; // in ms private var checkWaitTime:Boolean = false; // obey minimumDisplayTime only valid if splashImage is valid private var displayTimeStart:int = -1; // the start time of the image being displayed //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // backgroundAlpha //---------------------------------- /** * @private * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get backgroundAlpha():Number { return 0; } /** * @private */ public function set backgroundAlpha(value:Number):void { } //---------------------------------- // backgroundColor //---------------------------------- /** * @private * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get backgroundColor():uint { return 0; } /** * @private */ public function set backgroundColor(value:uint):void { } //---------------------------------- // backgroundImage //---------------------------------- /** * @private * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get backgroundImage():Object { return null; } /** * @private */ public function set backgroundImage(value:Object):void { } //---------------------------------- // backgroundSize //---------------------------------- /** * @private * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get backgroundSize():String { return null; } /** * @private */ public function set backgroundSize(value:String):void { } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // preloader //---------------------------------- /** * @copy mx.preloaders.DownloadProgressBar#preloader * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function set preloader(obj:Sprite):void { obj.addEventListener(FlexEvent.INIT_COMPLETE, preloader_initCompleteHandler, false /*useCapture*/, 0, true /*useWeakReference*/); } //---------------------------------- // stageHeight //---------------------------------- private var _stageHeight:Number; /** * @copy mx.preloaders.DownloadProgressBar#stageHeight * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get stageHeight():Number { return _stageHeight; } /** * @private */ public function set stageHeight(value:Number):void { _stageHeight = value; } //---------------------------------- // stageWidth //---------------------------------- private var _stageWidth:Number; /** * @copy mx.preloaders.DownloadProgressBar#stageWidth * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function get stageWidth():Number { return _stageWidth; } /** * @private */ public function set stageWidth(value:Number):void { _stageWidth = value; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * @copy mx.preloaders.DownloadProgressBar#initialize() * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ public function initialize():void { // The preloader parameters are in the SystemManager's info() object var sysManager:SystemManager = this.parent.loaderInfo.content as SystemManager; if (!sysManager) return; info = sysManager.info(); if (!info) return; // Add event listeners for resize. The first render will happen // after the first resize this.stage.addEventListener(Event.RESIZE, Stage_resizeHandler, false /*useCapture*/, 0, true /*useWeakReference*/); } /** * @private * Returns the class of the DisplayObject that should be instantiated * as the splash screen. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 2.5 * @productversion Flex 4.5 */ mx_internal function getImageClass(aspectRatio:String, dpi:Number, resolution:Number):Class { var sourceClass:Class; // If we don't have a dynamic source, then get the class from the info object if (!dynamicSource) { sourceClass = info["splashScreenImage"]; // Is this class a dynamicSource? if (sourceClass && !dynamicSourceAttempted) { dynamicSourceAttempted = true dynamicSource = new sourceClass() as SplashScreenImage; } } // If we have a dynamic source, call its method to get the appropriate class return dynamicSource ? dynamicSource.getImageClass(aspectRatio, dpi, resolution) : sourceClass; } private function prepareSplashScreen():void { // Grab the application's dpi provider class. If one doesn't exist, // use the framework's default provider var dpiProvider:RuntimeDPIProvider = ("runtimeDPIProvider" in info) ? new info["runtimeDPIProvider"]() : new RuntimeDPIProvider(); // Capture device dpi and orientation var dpi:Number = dpiProvider.runtimeDPI; var aspectRatio:String = (stage.stageWidth < stage.stageHeight) ? StageAspectRatio.PORTRAIT : StageAspectRatio.LANDSCAPE; var imageClass:Class = getImageClass(aspectRatio, dpi, Math.max(stage.stageWidth, stage.stageHeight)); // The SplashImageClass will only be set if a splash screen image has // already be generated. If the desired imageClass differs from the // current one, the splash screen should recreate the image with the // new class. if (imageClass && imageClass != SplashImageClass) { // The first time we create a splash screen, this will be null. // In this case, assign the initial splash screen properties if (!SplashImageClass) { if ("splashScreenScaleMode" in info) this.scaleMode = info["splashScreenScaleMode"]; // Since we have a valid image being displayed, we need to obey the minimumDisplayTime if ("splashScreenMinimumDisplayTime" in info) this.minimumDisplayTime = info["splashScreenMinimumDisplayTime"]; // Prepare the enterFrame handler for the minimum display time checkWaitTime = minimumDisplayTime > 0; if (checkWaitTime) this.addEventListener(Event.ENTER_FRAME, enterFrameHandler); } // Store the new class SplashImageClass = imageClass; // Remove the old splash image if (splashImage) removeChild(splashImage); // Create the image this.splashImage = new SplashImageClass(); this.splashImageWidth = splashImage.width; this.splashImageHeight = splashImage.height; addChildAt(splashImage as DisplayObject, 0); } } /** * How long has the image beein showing on screen? * For the minimumDisplayTime property. * @private */ private function get currentDisplayTime():int { if (-1 == displayTimeStart) return -1; return flash.utils.getTimer() - displayTimeStart; } //-------------------------------------------------------------------------- // // Event handlers // //-------------------------------------------------------------------------- /** * @private * Updates the splashImage matrix based on the scaleMode, stage dimensions and stage orientation. */ private function Stage_resizeHandler(event:Event):void { // This method will prepare the splash screen and create a // new instance if needed prepareSplashScreen(); if (!splashImage) return; // Current stage orientation var orientation:String = stage.orientation; // DPI scaling factor of the stage var dpiScale:Number = this.root.scaleX; // Get stage dimensions at default orientation var stageWidth:Number = stage.stageWidth / dpiScale; var stageHeight:Number = stage.stageHeight / dpiScale; // The image dimensions var width:Number = splashImageWidth; var height:Number = splashImageHeight; // Start building a matrix for the image var m:Matrix = new Matrix(); // Stretch var scaleX:Number = 1; var scaleY:Number = 1; switch(scaleMode) { case "zoom": scaleX = Math.max( stageWidth / width, stageHeight / height); scaleY = scaleX; break; case "letterbox": scaleX = Math.min( stageWidth / width, stageHeight / height); scaleY = scaleX; break; case "stretch": scaleX = stageWidth / width; scaleY = stageHeight / height; break; case "none": // SDK-30984: undo application's dpi scaling if we have a dynamic SplashScreen source if (dynamicSource) { scaleX = 1 / dpiScale; scaleY = 1 / dpiScale; } break; } if (scaleX != 1 || scaleY != 1) { width *= scaleX; height *= scaleY; m.scale(scaleX, scaleY); } // Move center to (0,0): m.translate(-width / 2, -height / 2); // Align center of image (0,0) to center of stage: m.translate(stageWidth / 2, stageHeight / 2); // Apply matrix splashImage.transform.matrix = m; } /** * Remembers when the splash image was visible for the first time. * For the minimumDisplayTime property. * @private */ private function enterFrameHandler(event:Event):void { this.displayTimeStart = flash.utils.getTimer(); this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler); } /** * @private * Called when the Application has finished initializing. */ private function preloader_initCompleteHandler(event:Event):void { // Do we have to wait? if (checkWaitTime && currentDisplayTime < minimumDisplayTime) this.addEventListener(Event.ENTER_FRAME, initCompleteEnterFrameHandler); else dispatchComplete(); } /** * @private * If the application is ready before the preloader minimumDisplayTime, * then this handler will be called on every ENTER_FRAME until the * minimumDisplayTime is reached, when it will dispatch a COMPLETE event * to let the Preloader class put up the Applicaiton on screen. */ private function initCompleteEnterFrameHandler(event:Event):void { if (currentDisplayTime <= minimumDisplayTime) return; dispatchComplete(); } private function dispatchComplete():void { // Clean-up all listeners var preloader:Preloader = this.parent as Preloader; preloader.removeEventListener(FlexEvent.INIT_COMPLETE, preloader_initCompleteHandler, false /*useCapture*/); this.removeEventListener(Event.ENTER_FRAME, initCompleteEnterFrameHandler); this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler); // Even though we have weak listeners, remove them since this object is not going to be destroyed until GC runs, // which means we could receive Stage events even if we're off-stage. this.stage.removeEventListener(Event.RESIZE, Stage_resizeHandler, false /*useCapture*/); this.stage.removeEventListener(StageOrientationEvent.ORIENTATION_CHANGE, Stage_resizeHandler, false); dispatchEvent(new Event(Event.COMPLETE)); } } }