From 0242773cae6cebd8c19f5f732ef811f1689b0f9e Mon Sep 17 00:00:00 2001 From: atanasovg Date: Wed, 14 May 2014 19:06:45 +0300 Subject: [PATCH] Refactored the Image module. Added image.fromUrl method. Added image-tests. --- BCL.csproj | 11 +++- Tests/TKUnit.ts | 27 ++++----- Tests/file_system_tests.ts | 3 +- Tests/image-tests.ts | 75 ++++++++++++++++++++++++ Tests/testRunner.ts | 2 + _references.ts | 4 +- filesystem/file_system.d.ts | 8 ++- filesystem/file_system.ts | 8 ++- image/image.android.ts | 94 ------------------------------ image/image.d.ts | 5 +- image/image.ios.ts | 81 -------------------------- image/image.ts | 110 ++++++++++++++++++++++++++++++++++++ image/image_impl.android.ts | 42 ++++++++++++++ image/image_impl.d.ts | 8 +++ image/image_impl.ios.ts | 33 +++++++++++ 15 files changed, 309 insertions(+), 202 deletions(-) create mode 100644 Tests/image-tests.ts delete mode 100644 image/image.android.ts delete mode 100644 image/image.ios.ts create mode 100644 image/image.ts create mode 100644 image/image_impl.android.ts create mode 100644 image/image_impl.d.ts create mode 100644 image/image_impl.ios.ts diff --git a/BCL.csproj b/BCL.csproj index 91c432e35..a57dcae94 100644 --- a/BCL.csproj +++ b/BCL.csproj @@ -122,12 +122,16 @@ file_system_access.d.ts - + image.d.ts - - image.d.ts + + image_impl.d.ts + + + + image_impl.d.ts @@ -142,6 +146,7 @@ + diff --git a/Tests/TKUnit.ts b/Tests/TKUnit.ts index 2e2990d82..5ffb553b9 100644 --- a/Tests/TKUnit.ts +++ b/Tests/TKUnit.ts @@ -70,7 +70,7 @@ export var runTestModule = function (module, moduleName) { console.info("--- " + moduleName + " TESTS COMPLETE --- (" + totalSuccess + " of " + totalTests + ") OK, " + (totalTests - totalSuccess) + " failed"); }; -export var assert = function (test: boolean, message?: string) { +export var assert = function (test: any, message?: string) { if (!test) { throw new Error(message); } @@ -85,11 +85,16 @@ export var wait = function (ms) { } }; -export var waitUntilReady = function (isReady, timeoutSec) { +export var waitUntilReady = function (isReady: () => boolean, timeoutSec?: number) { if (!isReady) { return; } + if (!timeoutSec) { + // TODO: How much should be the default timeout in seconds? + timeoutSec = 20; + } + if (Application.ios) { var waitTime = 20 / 1000; var totalWaitTime = 0; @@ -109,7 +114,7 @@ export var waitUntilReady = function (isReady, timeoutSec) { } }; -var doModalAndroid = function (quitLoop, timeoutSec) { +var doModalAndroid = function (quitLoop: () => boolean, timeoutSec: number) { if (!quitLoop) { return; } @@ -123,31 +128,27 @@ var doModalAndroid = function (quitLoop, timeoutSec) { for (i = 0; i < methods.length; i++) { if (methods[i].getName() === "next") { nextMethod = methods[i]; + nextMethod.setAccessible(true); break; } } - nextMethod.setAccessible(true); - var targetField; var fields = clsMsg.getDeclaredFields(); for (i = 0; i < fields.length; i++) { if (fields[i].getName() === "target") { targetField = fields[i]; + targetField.setAccessible(true); break; } } - targetField.setAccessible(true); - var queue = android.os.Looper.myQueue(); var quit = false; - if (timeoutSec) { - timer.setTimeout(function () { - quit = true; - }, timeoutSec * 1000); - } + timer.setTimeout(function () { + quit = true; + }, timeoutSec * 1000); var msg; @@ -163,7 +164,7 @@ var doModalAndroid = function (quitLoop, timeoutSec) { msg.recycle(); } - if (quitLoop()) { + if (!quit && quitLoop()) { quit = true; } } diff --git a/Tests/file_system_tests.ts b/Tests/file_system_tests.ts index 0357db8e6..99570dd1e 100644 --- a/Tests/file_system_tests.ts +++ b/Tests/file_system_tests.ts @@ -2,6 +2,7 @@ // // # File System // Using the file system requires the FileSystem module. +// TODO: var fs = require("filesystem"); => this will break the intellisense of the tests // ``` JavaScript import fs = require("filesystem/file_system"); // ``` @@ -347,7 +348,7 @@ export var testGetParent = function () { TKUnit.assert(file, "Failed to create file in the Documents folder."); // //// The parent folder of the file would be the documents folder. - var parent = file.getParent(); + var parent = file.parent; // TKUnit.assert(documents == parent, "The parent folder should be the Documents folder."); file.remove(); diff --git a/Tests/image-tests.ts b/Tests/image-tests.ts new file mode 100644 index 000000000..63de7f160 --- /dev/null +++ b/Tests/image-tests.ts @@ -0,0 +1,75 @@ +import image = require("image/image"); +import app = require("application/application"); +import fs = require("filesystem/file_system"); +import TKUnit = require("Tests/TKUnit"); + +export var testFromResource = function () { + var img = image.fromResource(getTestImageName()); + TKUnit.assert(img.height > 0, "image.fromResource failed"); +} + +export var testFromUrl = function () { + var completed; + var result: image.Image; + + image.fromUrl("http://www.google.com/images/errors/logo_sm_2.png") + .then(function (res: image.Image) { + completed = true; + result = res; + }) + .fail(function (error) { + completed = true; + }); + + var isReady = function () { + return completed; + } + + TKUnit.waitUntilReady(isReady, 3); + TKUnit.assert(typeof result !== "undefined", "Image not downloaded"); + TKUnit.assert(result.height > 0, "Image not downloaded"); +} + +export var testSaveToFile = function () { + var img = image.fromResource(getTestImageName()); + var folder = fs.knownFolders.documents(); + var path = fs.path.join(folder.path, "Test.png"); + + var saved = img.saveToFile(path, image.ImageFormat.PNG); + TKUnit.assert(saved, "Image not saved to file"); + TKUnit.assert(fs.File.exists(path), "Image not saved to file"); +} + +export var testFromFile = function () { + var folder = fs.knownFolders.documents(); + var path = fs.path.join(folder.path, "Test.png"); + + var img = image.fromFile(path); + + TKUnit.assert(img.height > 0, "image.fromResource failed"); + + // remove the image from the file system + var file = folder.getFile("Test.png"); + file.remove(); + TKUnit.assert(!fs.File.exists(path), "Test.png not removed"); +} + +export var testNativeFields = function () { + var img = image.fromResource(getTestImageName()); + if (app.android) { + TKUnit.assert(img.android != null, "Image.android not updated."); + } else if (app.ios) { + TKUnit.assert(img.ios != null, "Image.ios not updated."); + } +} + +var getTestImageName = function (): string { + if (app.ios) { + return "AppIcon"; + } + if (app.android) { + return "ic_launcher"; + } + + return ""; +} \ No newline at end of file diff --git a/Tests/testRunner.ts b/Tests/testRunner.ts index 98fe21038..fd9bca56b 100644 --- a/Tests/testRunner.ts +++ b/Tests/testRunner.ts @@ -3,8 +3,10 @@ var fsTests = require("Tests/file_system_tests"); var httpTests = require("Tests/http_tests"); var locationTests = require("Tests/location_tests"); var localSettingsTests = require("Tests/local_settings_tests"); +var imageTests = require("Tests/image-tests"); export var runAll = function () { + TKUnit.runTestModule(imageTests, "IMAGE"); TKUnit.runTestModule(fsTests, "FILE SYSTEM"); TKUnit.runTestModule(httpTests, "HTTP"); TKUnit.runTestModule(locationTests, "LOCATION"); diff --git a/_references.ts b/_references.ts index e361743f5..12d04708e 100644 --- a/_references.ts +++ b/_references.ts @@ -1,5 +1,3 @@ /// /// -/// -/// -/// \ No newline at end of file +/// \ No newline at end of file diff --git a/filesystem/file_system.d.ts b/filesystem/file_system.d.ts index 2bff296ed..0f81365f3 100644 --- a/filesystem/file_system.d.ts +++ b/filesystem/file_system.d.ts @@ -17,9 +17,11 @@ export declare class FileSystemEntity { public path: string; /** - * Gets the Folder object representing the parent of this entity. Will be null for a root folder like Documents or Temporary. - */ - public getParent(): Folder; + * Gets the Folder object representing the parent of this entity. + * Will be null for a root folder like Documents or Temporary. + * This property is readonly. + */ + public parent: Folder; /** * Removes (deletes) the current Entity from the file system. diff --git a/filesystem/file_system.ts b/filesystem/file_system.ts index 2d69d2a84..a30aa48f3 100644 --- a/filesystem/file_system.ts +++ b/filesystem/file_system.ts @@ -49,8 +49,10 @@ var createFolder = function (info: { path: string; name: string; }) { }; export class FileSystemEntity { - - public getParent(): Folder { + /** + * Gets the Folder object representing the parent of this entity. Will be null for a root folder like Documents or Temporary. + */ + get parent(): Folder { var onError = function (error) { throw error; } @@ -93,7 +95,7 @@ export class FileSystemEntity { } } - var parentFolder = this.getParent(); + var parentFolder = this.parent; if (!parentFolder) { deferred.reject(new Error("No parent folder.")); return deferred.promise(); diff --git a/image/image.android.ts b/image/image.android.ts deleted file mode 100644 index 3699d301f..000000000 --- a/image/image.android.ts +++ /dev/null @@ -1,94 +0,0 @@ -import appModule = require("application/application"); - -export enum ImageFormat { - PNG, - JPEG, -} - -export class Image { - public android: android.graphics.Bitmap; - - constructor() { - this.android = null; - } - - public loadFromResource(name: string): boolean { - var androidApp = appModule.android; - var res = androidApp.context.getResources(); - if (res) { - var identifier: number = res.getIdentifier(name, 'drawable', androidApp.packageName); - if (0 < identifier) { - this.android = android.graphics.BitmapFactory.decodeResource(res, identifier); - return (this.android != null); - } - } - return false; - } - - public loadFromFile(path: string): boolean { - this.android = android.graphics.BitmapFactory.decodeFile(path, null); - return (this.android != null); - } - - public loadFromData(data: any): boolean { - this.android = android.graphics.BitmapFactory.decodeStream(data); - return (this.android != null); - } - - public setNativeBitmap(source: any): boolean { - this.android = source; - return (this.android != null); - } - - public saveToFile(path: string, format: ImageFormat, quality?: number): boolean { - if (this.android) { - var targetFormat = android.graphics.Bitmap.CompressFormat.PNG; - switch (format) { - case ImageFormat.JPEG: - targetFormat = android.graphics.Bitmap.CompressFormat.JPEG; - break; - } - - // TODO add exception handling - var outputStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(path)); - - if (typeof quality == "undefined") { - quality = 100; - } - - var res = this.android.compress(targetFormat, quality, outputStream); - outputStream.close(); - return res; - } - return false; - } - - get height(): number { - return (this.android) ? this.android.getHeight() : NaN; - } - - get width(): number { - return (this.android) ? this.android.getWidth() : NaN; - } -} - -// TODO: These functions are the same in each platform, think for some common code separation -export function fromResource(name: string): Image { - var image = new Image(); - return image.loadFromResource(name) ? image : null; -} - -export function fromFile(path: string): Image { - var image = new Image(); - return image.loadFromFile(path) ? image : null; -} - -export function fromData(data: any): Image { - var image = new Image(); - return image.loadFromData(data) ? image : null; -} - -export function fromNativeBitmap(source: any): Image { - var image = new Image(); - return image.setNativeBitmap(source) ? image : null; -} \ No newline at end of file diff --git a/image/image.d.ts b/image/image.d.ts index 0d3aa1411..b1477ff1a 100644 --- a/image/image.d.ts +++ b/image/image.d.ts @@ -80,4 +80,7 @@ export declare function fromData(data: any): Image; */ export declare function fromNativeBitmap(source: any): Image; -// export declare function fromUrl \ No newline at end of file +/** +* Downloads the image from the provided Url and creates a new Image instance from it. +*/ +export declare function fromUrl(url: string): promises.Promise; \ No newline at end of file diff --git a/image/image.ios.ts b/image/image.ios.ts deleted file mode 100644 index 2df1493cc..000000000 --- a/image/image.ios.ts +++ /dev/null @@ -1,81 +0,0 @@ -export enum ImageFormat { - PNG, - JPEG, -} - -export class Image { - public ios: UIKit.UIImage; - - constructor() { - this.ios = null; - } - - public loadFromResource(name: string): boolean { - this.ios = UIKit.UIImage.imageNamed(name); - return (this.ios != null); - } - - public loadFromFile(path: string): boolean { - this.ios = UIKit.UIImage.imageWithContentsOfFile(path); - return (this.ios != null); - } - - public loadFromData(data: any): boolean { - this.ios = UIKit.UIImage.imageWithData(data); - return (this.ios != null); - } - - public setNativeBitmap(source: any): boolean { - this.ios = source; - return (this.ios != null); - } - - public saveToFile(path: string, format: ImageFormat, quality?: number): boolean { - if (null == this.ios) { - return false; - } - var res = false; - var data = null; - switch (format) { - case ImageFormat.JPEG: - data = UIKit.UIImageJPEGRepresentation(this.ios, ('undefined' == typeof quality) ? 1.0 : quality); - break; - case ImageFormat.PNG: - data = UIKit.UIImagePNGRepresentation(this.ios); - break; - } - if (null != data) { - res = data.writeToFileAtomically(path, true); - } - return res; - } - - get height(): number { - return (this.ios) ? this.ios.size.height : NaN; - } - - get width(): number { - return (this.ios) ? this.ios.size.width : NaN; - } -} - -// TODO: These functions are the same in each platform, think for some common code separation -export function fromResource(name: string): Image { - var image = new Image(); - return image.loadFromResource(name) ? image : null; -} - -export function fromFile(path: string): Image { - var image = new Image(); - return image.loadFromFile(path) ? image : null; -} - -export function fromData(data: any): Image { - var image = new Image(); - return image.loadFromData(data) ? image : null; -} - -export function fromNativeBitmap(source: any): Image { - var image = new Image(); - return image.setNativeBitmap(source) ? image : null; -} \ No newline at end of file diff --git a/image/image.ts b/image/image.ts new file mode 100644 index 000000000..7d9efa391 --- /dev/null +++ b/image/image.ts @@ -0,0 +1,110 @@ +import app = require("application/application"); +import impl = require("image/image_impl"); +import promises = require("promises/promises"); +import http = require("http/http"); + +export enum ImageFormat { + PNG, + JPEG, +} + +export class Image { + public android: android.graphics.Bitmap; + public ios: UIKit.UIImage; + + constructor() { + this.setNativeInstance(null); + } + + public loadFromResource(name: string): boolean { + var nativeInstance = impl.fromResource(name); + this.setNativeInstance(nativeInstance); + return nativeInstance != null; + } + + public loadFromFile(path: string): boolean { + var nativeInstance = impl.fromFile(path); + this.setNativeInstance(nativeInstance); + return (nativeInstance != null); + } + + public loadFromData(data: any): boolean { + var nativeInstance = impl.fromData(data); + this.setNativeInstance(nativeInstance); + return (nativeInstance != null); + } + + public setNativeBitmap(source: any): boolean { + this.setNativeInstance(source); + return source != null; + } + + public saveToFile(path: string, format: ImageFormat, quality?: number): boolean { + return impl.saveToFile(this.getNativeInstance(), path, format, quality); + } + + get height(): number { + if (this.android) { + return this.android.getHeight(); + } + if (this.ios) { + return this.ios.size.height; + } + + return NaN; + } + + get width(): number { + if (this.android) { + return this.android.getWidth(); + } + if (this.ios) { + return this.ios.size.width; + } + + return NaN; + } + + private setNativeInstance(instance: any) { + if (app.android) { + this.android = instance; + } else if (app.ios) { + this.ios = instance; + } + } + + private getNativeInstance(): any { + if (this.android) { + return this.android; + } + if (this.ios) { + return this.ios; + } + + return undefined; + } +} + +export function fromResource(name: string): Image { + var image = new Image(); + return image.loadFromResource(name) ? image : null; +} + +export function fromFile(path: string): Image { + var image = new Image(); + return image.loadFromFile(path) ? image : null; +} + +export function fromData(data: any): Image { + var image = new Image(); + return image.loadFromData(data) ? image : null; +} + +export function fromNativeBitmap(source: any): Image { + var image = new Image(); + return image.setNativeBitmap(source) ? image : null; +} + +export function fromUrl(url: string): promises.Promise { + return http.getImage(url); +} \ No newline at end of file diff --git a/image/image_impl.android.ts b/image/image_impl.android.ts new file mode 100644 index 000000000..7b9c969f2 --- /dev/null +++ b/image/image_impl.android.ts @@ -0,0 +1,42 @@ +import appModule = require("application/application"); + +export var fromResource = function (name: string) { + var androidApp = appModule.android; + var res = androidApp.context.getResources(); + if (res) { + var identifier: number = res.getIdentifier(name, 'drawable', androidApp.packageName); + if (0 < identifier) { + return android.graphics.BitmapFactory.decodeResource(res, identifier); + } + } + + return null; +} + +export var fromFile = function (path: string) { + return android.graphics.BitmapFactory.decodeFile(path, null); +} + +export var fromData = function (data: any) { + return android.graphics.BitmapFactory.decodeStream(data); +} + +export var saveToFile = function (instance: android.graphics.Bitmap, path: string, format: number, quality = 100): boolean { + if (!instance) { + return false; + } + + var targetFormat = android.graphics.Bitmap.CompressFormat.PNG; + switch (format) { + case 1: // JPEG + targetFormat = android.graphics.Bitmap.CompressFormat.JPEG; + break; + } + + // TODO add exception handling + var outputStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(path)); + + var res = instance.compress(targetFormat, quality, outputStream); + outputStream.close(); + return res; +} \ No newline at end of file diff --git a/image/image_impl.d.ts b/image/image_impl.d.ts new file mode 100644 index 000000000..3e3646ecf --- /dev/null +++ b/image/image_impl.d.ts @@ -0,0 +1,8 @@ +/** +* This module is used as a native implementation for each of the underlying platforms. +* Users will not typically require it as it supports the module infrastructure. +*/ +export declare function fromResource(name: string): any; +export declare function fromFile(path: string): any; +export declare function fromData(data: any): any; +export declare function saveToFile(instance: any, path: string, format: number, quality?: number): boolean; \ No newline at end of file diff --git a/image/image_impl.ios.ts b/image/image_impl.ios.ts new file mode 100644 index 000000000..a5c73c9b3 --- /dev/null +++ b/image/image_impl.ios.ts @@ -0,0 +1,33 @@ +export var fromResource = function (name: string) { + return UIKit.UIImage.imageNamed(name); +} + +export var fromFile = function (path: string) { + return UIKit.UIImage.imageWithContentsOfFile(path); +} + +export var fromData = function (data: any) { + return UIKit.UIImage.imageWithData(data); +} + +export var saveToFile = function (instance: UIKit.UIImage, path: string, format: number, quality?: number): boolean { + if (!instance) { + return false; + } + + var res = false; + var data = null; + switch (format) { + case 0: // PNG + data = UIKit.UIImagePNGRepresentation(instance); + break; + case 1: // JPEG + data = UIKit.UIImageJPEGRepresentation(instance, ('undefined' == typeof quality) ? 1.0 : quality); + break; + + } + if (null != data) { + res = data.writeToFileAtomically(path, true); + } + return res; +} \ No newline at end of file