// Definitions. import { ImageSource as ImageSourceDefinition } from "."; import { ImageAsset } from "../image-asset"; import * as httpModule from "../http"; // Types. import { path as fsPath, knownFolders } from "../file-system"; import { isFileOrResourcePath, RESOURCE_PREFIX, layout } from "../utils/utils"; import { getNativeApplication } from "../application"; import { Font } from "../ui/styling/font"; import { Color } from "../color"; export { isFileOrResourcePath }; let http: typeof httpModule; function ensureHttp() { if (!http) { http = require("../http"); } } let application: android.app.Application; let resources: android.content.res.Resources; function getApplication() { if (!application) { application = (getNativeApplication()); } return application; } function getResources() { if (!resources) { resources = getApplication().getResources(); } return resources; } export class ImageSource implements ImageSourceDefinition { public android: android.graphics.Bitmap; public ios: UIImage; public fromAsset(asset: ImageAsset): Promise { return new Promise((resolve, reject) => { asset.getImageAsync((image, err) => { if (image) { this.setNativeSource(image); resolve(this); } else { reject(err); } }); }); } public loadFromResource(name: string): boolean { this.android = null; const res = getResources(); if (res) { const identifier: number = res.getIdentifier(name, "drawable", getApplication().getPackageName()); if (0 < identifier) { // Load BitmapDrawable with getDrawable to make use of Android internal caching const bitmapDrawable = res.getDrawable(identifier); if (bitmapDrawable && bitmapDrawable.getBitmap) { this.android = bitmapDrawable.getBitmap(); } } } return this.android != null; } public fromResource(name: string): Promise { return new Promise((resolve, reject) => { resolve(this.loadFromResource(name)); }); } private setRotationAngleFromFile(filename: string) { this.rotationAngle = 0; const ei = new android.media.ExifInterface(filename); const orientation = ei.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, android.media.ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case android.media.ExifInterface.ORIENTATION_ROTATE_90: this.rotationAngle = 90; break; case android.media.ExifInterface.ORIENTATION_ROTATE_180: this.rotationAngle = 180; break; case android.media.ExifInterface.ORIENTATION_ROTATE_270: this.rotationAngle = 270; break; } } public loadFromFile(path: string): boolean { let fileName = typeof path === "string" ? path.trim() : ""; if (fileName.indexOf("~/") === 0) { fileName = fsPath.join(knownFolders.currentApp().path, fileName.replace("~/", "")); } this.setRotationAngleFromFile(fileName); this.android = android.graphics.BitmapFactory.decodeFile(fileName, null); return this.android != null; } public fromFile(path: string): Promise { return new Promise((resolve, reject) => { resolve(this.loadFromFile(path)); }); } public loadFromData(data: any): boolean { this.android = android.graphics.BitmapFactory.decodeStream(data); return this.android != null; } public fromData(data: any): Promise { return new Promise((resolve, reject) => { resolve(this.loadFromData(data)); }); } public loadFromBase64(source: string): boolean { if (typeof source === "string") { const bytes = android.util.Base64.decode(source, android.util.Base64.DEFAULT); this.android = android.graphics.BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } return this.android != null; } public fromBase64(data: any): Promise { return new Promise((resolve, reject) => { resolve(this.loadFromBase64(data)); }); } public loadFromFontIconCode(source: string, font: Font, color: Color): boolean { const paint = new android.graphics.Paint(); paint.setTypeface(font.getAndroidTypeface()); paint.setAntiAlias(true); if (color) { paint.setColor(color.android); } let fontSize = layout.toDevicePixels(font.fontSize); if (!fontSize) { // TODO: Consider making 36 font size as default for optimal look on TabView and ActionBar fontSize = paint.getTextSize(); } const density = layout.getDisplayDensity(); const scaledFontSize = fontSize * density; paint.setTextSize(scaledFontSize); const textBounds = new android.graphics.Rect(); paint.getTextBounds(source, 0, source.length, textBounds); const textWidth = textBounds.width(); const textHeight = textBounds.height(); if (textWidth > 0 && textHeight > 0) { const bitmap = android.graphics.Bitmap .createBitmap( textWidth, textHeight, android.graphics.Bitmap.Config.ARGB_8888 ); const canvas = new android.graphics.Canvas(bitmap); canvas.drawText(source, -textBounds.left, -textBounds.top, paint); this.android = bitmap; } return this.android != null; } public setNativeSource(source: any): void { if (source && !(source instanceof android.graphics.Bitmap)) { throw new Error("The method setNativeSource() expects android.graphics.Bitmap instance."); } this.android = source; } public saveToFile(path: string, format: "png" | "jpeg" | "jpg", quality = 100): boolean { if (!this.android) { return false; } const targetFormat = getTargetFormat(format); // TODO add exception handling const outputStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(path)); const res = this.android.compress(targetFormat, quality, outputStream); outputStream.close(); return res; } public toBase64String(format: "png" | "jpeg" | "jpg", quality = 100): string { if (!this.android) { return null; } const targetFormat = getTargetFormat(format); const outputStream = new java.io.ByteArrayOutputStream(); const base64Stream = new android.util.Base64OutputStream(outputStream, android.util.Base64.NO_WRAP); this.android.compress(targetFormat, quality, base64Stream); base64Stream.close(); outputStream.close(); return outputStream.toString(); } get height(): number { if (this.android) { return this.android.getHeight(); } return NaN; } get width(): number { if (this.android) { return this.android.getWidth(); } return NaN; } private _rotationAngle: number; get rotationAngle(): number { return this._rotationAngle; } set rotationAngle(value: number) { this._rotationAngle = value; } } function getTargetFormat(format: "png" | "jpeg" | "jpg"): android.graphics.Bitmap.CompressFormat { switch (format) { case "jpeg": case "jpg": return android.graphics.Bitmap.CompressFormat.JPEG; default: return android.graphics.Bitmap.CompressFormat.PNG; } } export function fromAsset(asset: ImageAsset): Promise { const image = new ImageSource(); return image.fromAsset(asset); } export function fromResource(name: string): ImageSource { const image = new ImageSource(); return image.loadFromResource(name) ? image : null; } export function fromFile(path: string): ImageSource { const image = new ImageSource(); return image.loadFromFile(path) ? image : null; } export function fromData(data: any): ImageSource { const image = new ImageSource(); return image.loadFromData(data) ? image : null; } export function fromFontIconCode(source: string, font: Font, color: Color): ImageSource { const image = new ImageSource(); return image.loadFromFontIconCode(source, font, color) ? image : null; } export function fromBase64(source: string): ImageSource { const image = new ImageSource(); return image.loadFromBase64(source) ? image : null; } export function fromNativeSource(source: any): ImageSource { const imageSource = new ImageSource(); imageSource.setNativeSource(source); return imageSource; } export function fromUrl(url: string): Promise { ensureHttp(); return http.getImage(url); } export function fromFileOrResource(path: string): ImageSource { if (!isFileOrResourcePath(path)) { throw new Error(`${path} is not a valid file or resource.`); } if (path.indexOf(RESOURCE_PREFIX) === 0) { return fromResource(path.substr(RESOURCE_PREFIX.length)); } return fromFile(path); }