aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJorge Ruesga <jorge@ruesga.com>2014-10-01 20:36:47 +0000
committerGerrit Code Review <gerrit@cyanogenmod.org>2014-10-01 20:36:47 +0000
commita2e8778cc0742fa13a940c417c155136c77b5682 (patch)
treefbb51c772fe7ac98c951ccde8c2c6cfd05594bc1
parent4276df4ec1bf6270b27429fae7190a7af64831e7 (diff)
parentbc60c88e33f0f0d1a068e8605f2de2c5121b9916 (diff)
downloadandroid_packages_apps_CMFileManager-a2e8778cc0742fa13a940c417c155136c77b5682.tar.gz
android_packages_apps_CMFileManager-a2e8778cc0742fa13a940c417c155136c77b5682.tar.bz2
android_packages_apps_CMFileManager-a2e8778cc0742fa13a940c417c155136c77b5682.zip
Merge "cmfm: print support" into cm-11.0
-rw-r--r--assets/fonts/Courier-Prime.ttfbin0 -> 98156 bytes
-rw-r--r--res/menu/actions.xml4
-rw-r--r--res/values/dimen.xml5
-rw-r--r--res/values/strings.xml12
-rw-r--r--src/com/cyanogenmod/filemanager/activities/EditorActivity.java22
-rw-r--r--src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java12
-rw-r--r--src/com/cyanogenmod/filemanager/ui/policy/PrintActionPolicy.java549
-rw-r--r--src/com/cyanogenmod/filemanager/util/StringHelper.java103
8 files changed, 687 insertions, 20 deletions
diff --git a/assets/fonts/Courier-Prime.ttf b/assets/fonts/Courier-Prime.ttf
new file mode 100644
index 00000000..db4e6c14
--- /dev/null
+++ b/assets/fonts/Courier-Prime.ttf
Binary files differ
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index 305c6bba..ba5f50cf 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -131,6 +131,10 @@
android:showAsAction="ifRoom"
android:title="@string/actions_menu_send"/>
<item
+ android:id="@+id/mnu_actions_print"
+ android:showAsAction="ifRoom"
+ android:title="@string/actions_menu_print"/>
+ <item
android:id="@+id/mnu_actions_add_to_bookmarks"
android:showAsAction="ifRoom"
android:title="@string/actions_menu_add_to_bookmarks"/>
diff --git a/res/values/dimen.xml b/res/values/dimen.xml
index 91eb40da..a597dc13 100644
--- a/res/values/dimen.xml
+++ b/res/values/dimen.xml
@@ -114,4 +114,9 @@
<!-- Theme width/height -->
<dimen name="theme_max_width">300dip</dimen>
<dimen name="theme_max_height">600dip</dimen>
+
+ <!-- Print text size -->
+ <dimen name="print_text_size">6sp</dimen>
+ <!-- Print page margins -->
+ <dimen name="print_page_margins">5dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d353bcd7..2c51d02f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -474,6 +474,8 @@
<string name="actions_menu_open_parent_folder">Open parent</string>
<!-- Actions Dialog - Menu - Compute checksum -->
<string name="actions_menu_compute_checksum">Compute checksum</string>
+ <!-- Actions Dialog - Menu - Print -->
+ <string name="actions_menu_print">Print</string>
<!-- Actions - Ask user prior to do an undone operation. Dialog message -->
<string name="actions_ask_undone_operation_msg">This action cannot be undone. Do you want to continue?</string>
@@ -723,6 +725,16 @@
<string name="ash_quoted_string">Quoted string</string>
<string name="ash_variable">Variable</string>
+ <!-- Print messages -->
+ <!-- Unsupported document format -->
+ <string name="print_unsupported_document">Unsupported document format</string>
+ <!-- Unsupported image format -->
+ <string name="print_unsupported_image">Unsupported image format</string>
+ <!-- Print header -->
+ <string name="print_document_header">Document: <xliff:g id="document_name">%1$s</xliff:g></string>
+ <!-- Print footer -->
+ <string name="print_document_footer">Page <xliff:g id="page_number">%1$s</xliff:g></string>
+
<!-- Security - Extract relative or absolute files -->
<string name="security_warning_extract">Warning!\n\nExtracting an archive file with relative or absolute paths may cause damage to your device by overwriting system files.\n\nDo you want to continue?</string>
diff --git a/src/com/cyanogenmod/filemanager/activities/EditorActivity.java b/src/com/cyanogenmod/filemanager/activities/EditorActivity.java
index 26b048e8..1ccf3488 100644
--- a/src/com/cyanogenmod/filemanager/activities/EditorActivity.java
+++ b/src/com/cyanogenmod/filemanager/activities/EditorActivity.java
@@ -84,6 +84,7 @@ import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
import com.cyanogenmod.filemanager.util.FileHelper;
import com.cyanogenmod.filemanager.util.MediaHelper;
import com.cyanogenmod.filemanager.util.ResourcesHelper;
+import com.cyanogenmod.filemanager.util.StringHelper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
@@ -304,7 +305,7 @@ public class EditorActivity extends Activity implements TextWatcher {
// is read-only
if (!EditorActivity.this.mReadOnly) {
for (int i = 0; i < partial.length-1; i++) {
- if (!isPrintableCharacter((char)partial[i])) {
+ if (!StringHelper.isPrintableCharacter((char)partial[i])) {
EditorActivity.this.mBinary = true;
EditorActivity.this.mReadOnly = true;
break;
@@ -550,8 +551,6 @@ public class EditorActivity extends Activity implements TextWatcher {
*/
Handler mHandler;
- private static final char[] VALID_NON_PRINTABLE_CHARS = {' ', '\t', '\r', '\n'};
-
/**
* @hide
*/
@@ -1508,23 +1507,6 @@ public class EditorActivity extends Activity implements TextWatcher {
}
/**
- * Method that check if a character is valid printable character
- *
- * @param c The character to check
- * @return boolean If the character is printable
- * @hide
- */
- static boolean isPrintableCharacter(char c) {
- int cc = VALID_NON_PRINTABLE_CHARS.length;
- for (int i = 0; i < cc; i++) {
- if (c == VALID_NON_PRINTABLE_CHARS[i]) {
- return true;
- }
- }
- return TextUtils.isGraphic(c);
- }
-
- /**
* Method that applies the current theme to the activity
* @hide
*/
diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
index bb34dceb..cb2ceab4 100644
--- a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
+++ b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
@@ -55,6 +55,7 @@ import com.cyanogenmod.filemanager.ui.policy.InfoActionPolicy;
import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
import com.cyanogenmod.filemanager.ui.policy.NavigationActionPolicy;
import com.cyanogenmod.filemanager.ui.policy.NewActionPolicy;
+import com.cyanogenmod.filemanager.ui.policy.PrintActionPolicy;
import com.cyanogenmod.filemanager.util.DialogHelper;
import com.cyanogenmod.filemanager.util.FileHelper;
import com.cyanogenmod.filemanager.util.MimeTypeHelper;
@@ -410,6 +411,11 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
InfoActionPolicy.showComputeChecksumDialog(this.mContext, this.mFso);
break;
+ //- Print
+ case R.id.mnu_actions_print:
+ PrintActionPolicy.printDocument(this.mContext, this.mFso);
+ break;
+
//- Properties
case R.id.mnu_actions_properties:
case R.id.mnu_actions_properties_current_folder:
@@ -635,6 +641,12 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
if (FileHelper.isDirectory(this.mFso) || this.mFso instanceof Symlink) {
menu.removeItem(R.id.mnu_actions_compute_checksum);
}
+
+ //- Print (only for text and image categories)
+ if (category.compareTo(MimeTypeCategory.TEXT) != 0 &&
+ category.compareTo(MimeTypeCategory.IMAGE) != 0) {
+ menu.removeItem(R.id.mnu_actions_print);
+ }
}
//- Add to bookmarks -> Only directories
diff --git a/src/com/cyanogenmod/filemanager/ui/policy/PrintActionPolicy.java b/src/com/cyanogenmod/filemanager/ui/policy/PrintActionPolicy.java
new file mode 100644
index 00000000..13d08cd2
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/ui/policy/PrintActionPolicy.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed 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 com.cyanogenmod.filemanager.ui.policy;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.pdf.PdfDocument.Page;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.pdf.PrintedPdfDocument;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.util.DialogHelper;
+import com.cyanogenmod.filemanager.util.ExceptionUtil;
+import com.cyanogenmod.filemanager.util.MimeTypeHelper;
+import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
+import com.cyanogenmod.filemanager.util.StringHelper;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class with the convenience methods to print documents
+ */
+public final class PrintActionPolicy extends ActionsPolicy {
+
+ private static final String TAG = "PrintActionPolicy"; //$NON-NLS-1$
+
+ /**
+ * Method that prints the passed document
+ *
+ * @param ctx The current context
+ * @param fso The document to print
+ */
+ public static void printDocument(final Context ctx, FileSystemObject fso) {
+ MimeTypeCategory category = MimeTypeHelper.getCategory(ctx, fso);
+ if (category.equals(MimeTypeCategory.TEXT)) {
+ printTextDocument(ctx, fso);
+ return;
+ }
+ if (category.equals(MimeTypeCategory.IMAGE)) {
+ printImage(ctx, fso);
+ return;
+ }
+ DialogHelper.showToast(ctx, R.string.print_unsupported_document, Toast.LENGTH_SHORT);
+ }
+
+ /**
+ * Method that prints the document as a text document
+ *
+ * @param ctx The current context
+ * @param fso The document to print
+ */
+ private static void printTextDocument(final Context ctx, final FileSystemObject document) {
+ final int printPageMargins = ctx.getResources().getDimensionPixelSize(
+ R.dimen.print_page_margins);
+
+ PrintManager printManager = (PrintManager) ctx.getSystemService(Context.PRINT_SERVICE);
+ PrintAttributes attr = new PrintAttributes.Builder()
+ .setMediaSize(PrintAttributes.MediaSize.UNKNOWN_PORTRAIT)
+ .setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
+ .build();
+ printManager.print(document.getName(), new PrintDocumentAdapter() {
+ private PrintAttributes mAttributes;
+ private Paint mPaint;
+ private RectF mTextBounds;
+ private boolean mIsBinaryDocument;
+ private List<String> mLines;
+ private List<String> mAdjustedLines;
+
+ private static final int MILS_PER_INCH = 1000;
+ private static final int POINTS_IN_INCH = 72;
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // Create the paint used for draw text
+ Typeface courier = Typeface.createFromAsset(ctx.getAssets(),
+ "fonts/Courier-Prime.ttf");
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setTypeface(courier);
+ mPaint.setTextSize(ctx.getResources().getDimensionPixelSize(
+ R.dimen.print_text_size));
+ mPaint.setColor(Color.BLACK);
+
+ // Get the text width and height
+ mTextBounds = new RectF();
+ mTextBounds.right = mPaint.measureText(new char[]{'A'}, 0, 1);
+ mTextBounds.bottom = mPaint.getFontMetrics().descent
+ - mPaint.getFontMetrics().ascent + mPaint.getFontMetrics().leading;
+
+ mLines = new ArrayList<String>();
+ readFile();
+ }
+
+ @Override
+ public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
+ PrintedPdfDocument pdfDocument = new PrintedPdfDocument(ctx,
+ mAttributes);
+ try {
+ Rect pageContentRect = getContentRect(mAttributes);
+ int charsPerRow = (int) (pageContentRect.width() / mTextBounds.width());
+ int rowsPerPage = rowsPerPage(pageContentRect);
+
+ int currentPage = 0;
+ int currentLine = 0;
+ Page page = null;
+ if (mAdjustedLines.size() > 0) {
+ page = pdfDocument.startPage(currentPage++);
+ printHeader(ctx, page, pageContentRect, charsPerRow);
+ }
+ // Top (with margin) + header
+ float top = pageContentRect.top + (mTextBounds.height() * 2);
+ for (String line : mAdjustedLines) {
+ currentLine++;
+ page.getCanvas().drawText(line, pageContentRect.left,
+ top + (currentLine * mTextBounds.height()), mPaint);
+
+ if (currentLine >= rowsPerPage) {
+ if (page != null) {
+ printFooter(ctx, page, pageContentRect, currentPage);
+ pdfDocument.finishPage(page);
+ }
+ currentLine = 0;
+ page = pdfDocument.startPage(currentPage++);
+ printHeader(ctx, page, pageContentRect, charsPerRow);
+ }
+ }
+
+ // Finish the last page
+ printFooter(ctx, page, pageContentRect, currentPage);
+ pdfDocument.finishPage(page);
+
+ try {
+ // Write the document
+ pdfDocument.writeTo(new FileOutputStream(destination.getFileDescriptor()));
+
+ // Done
+ callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
+ } catch (IOException ioe) {
+ // Failed.
+ ExceptionUtil.translateException(ctx, ioe);
+ callback.onWriteFailed(null);
+ }
+ } finally {
+ if (destination != null) {
+ try {
+ destination.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle extras) {
+
+ mAttributes = newAttributes;
+ Rect pageContentRect = getContentRect(newAttributes);
+ int charsPerRow = (int) (pageContentRect.width() / mTextBounds.width());
+ int rowsPerPage = rowsPerPage(pageContentRect);
+ adjustLines(pageContentRect, charsPerRow);
+
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(document.getName())
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+ .setPageCount(calculatePageCount(rowsPerPage))
+ .build();
+ info.setDataSize(document.getSize());
+ boolean changed = !newAttributes.equals(oldAttributes);
+ callback.onLayoutFinished(info, changed);
+ }
+
+ private Rect getContentRect(PrintAttributes attributes) {
+ MediaSize mediaSize = attributes.getMediaSize();
+
+ // Compute the size of the target canvas from the attributes.
+ int pageWidth = (int) (((float) mediaSize.getWidthMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+ int pageHeight = (int) (((float) mediaSize.getHeightMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+
+ // Compute the content size from the attributes.
+ Margins minMargins = attributes.getMinMargins();
+ final int marginLeft = (int) (((float) minMargins.getLeftMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+ final int marginTop = (int) (((float) minMargins.getTopMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+ final int marginRight = (int) (((float) minMargins.getRightMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+ final int marginBottom = (int) (((float) minMargins.getBottomMils() / MILS_PER_INCH)
+ * POINTS_IN_INCH);
+ return new Rect(
+ Math.max(marginLeft, printPageMargins),
+ Math.max(marginTop, printPageMargins),
+ pageWidth - Math.max(marginRight, printPageMargins),
+ pageHeight - Math.max(marginBottom, printPageMargins));
+ }
+
+ private void printHeader(Context ctx, Page page, Rect pageContentRect,
+ int charsPerRow) {
+ String header = ctx.getString(R.string.print_document_header, document.getName());
+ if (header.length() >= charsPerRow) {
+ header = header.substring(header.length() - 3) + "...";
+ }
+ page.getCanvas().drawText(header,
+ (int) (pageContentRect.width() / 2) - (mPaint.measureText(header) / 2),
+ pageContentRect.top + mTextBounds.height(), mPaint);
+ }
+
+ private void printFooter(Context ctx, Page page, Rect pageContentRect, int pageNumber) {
+ String footer = ctx.getString(R.string.print_document_footer, pageNumber);
+ page.getCanvas().drawText(footer,
+ (int) (pageContentRect.width() / 2) - (mPaint.measureText(footer) / 2),
+ pageContentRect.bottom - mTextBounds.height(), mPaint);
+ }
+
+ private void adjustLines(Rect pageRect, int charsPerRow) {
+ if (mIsBinaryDocument) {
+ return;
+ }
+ mAdjustedLines = new ArrayList<String>(mLines);
+ for (int i = 0; i < mAdjustedLines.size(); i++) {
+ String line = mAdjustedLines.get(i);
+ if (line.length() > charsPerRow) {
+ int prevSpace = line.lastIndexOf(" ", charsPerRow);
+ if (prevSpace != -1) {
+ // Split in the previous word
+ String currentLine = line.substring(0, prevSpace + 1);
+ String nextLine = line.substring(prevSpace + 1);
+ mAdjustedLines.set(i, currentLine);
+ mAdjustedLines.add(i + 1, nextLine);
+ } else {
+ // Just split at margin
+ String currentLine = line.substring(0, charsPerRow);
+ String nextLine = line.substring(charsPerRow);
+ mAdjustedLines.set(i, currentLine);
+ mAdjustedLines.add(i + 1, nextLine);
+ }
+ }
+ }
+ }
+
+ private int calculatePageCount(int rowsPerPage) {
+ int pages = mAdjustedLines.size() / rowsPerPage;
+ return pages <= 0 ? PrintDocumentInfo.PAGE_COUNT_UNKNOWN : pages;
+ }
+
+ private int rowsPerPage(Rect pageContentRect) {
+ // Text height - header - footer
+ return (int) ((pageContentRect.height() / mTextBounds.height()) - 4);
+ }
+
+ private void readFile() {
+ mIsBinaryDocument = isBinaryDocument();
+ if (mIsBinaryDocument) {
+ readHexDumpDocumentFile();
+ } else {
+ readDocumentFile();
+ }
+ }
+
+ private boolean isBinaryDocument() {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(document.getFullPath()));
+ char[] data = new char[50];
+ int read = br.read(data);
+ for (int i = 0; i < read; i++) {
+ if (!StringHelper.isPrintableCharacter(data[i])) {
+ return true;
+ }
+ }
+ } catch (IOException ex) {
+ //Ignore
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ex) {
+ //Ignore
+ }
+ }
+ }
+ return false;
+ }
+
+ private void readDocumentFile() {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(document.getFullPath()));
+ String line = null;
+ while((line = br.readLine()) != null) {
+ mLines.add(line);
+ }
+ } catch (IOException ex) {
+ mLines.clear();
+ Log.e(TAG, "Failed to read file " + document.getFullPath(), ex);
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ex) {
+ //Ignore
+ }
+ }
+ }
+ }
+
+ private void readHexDumpDocumentFile() {
+ InputStream is = null;
+ ByteArrayOutputStream baos;
+ try {
+ int bufferSize = ctx.getResources().getInteger(R.integer.buffer_size);
+
+ baos = new ByteArrayOutputStream();
+ is = new BufferedInputStream(new FileInputStream(document.getFullPath()));
+ byte[] data = new byte[bufferSize];
+ int read = 0;
+ while((read = is.read(data, 0, bufferSize)) != -1) {
+ baos.write(data, 0, read);
+ }
+ } catch (IOException ex) {
+ mLines.clear();
+ Log.e(TAG, "Failed to read file " + document.getFullPath(), ex);
+ return;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException ex) {
+ //Ignore
+ }
+ }
+ }
+
+ // Convert the bytes to a hex printable string and free resources
+ String documentBuffer = StringHelper.toHexPrintableString(baos.toByteArray());
+ try {
+ baos.close();
+ } catch (IOException ex) {
+ //Ignore
+ }
+
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new StringReader(documentBuffer));
+ String line = null;
+ while((line = br.readLine()) != null) {
+ mLines.add(line);
+ }
+ } catch (IOException ex) {
+ mLines.clear();
+ Log.e(TAG, "Failed to read file " + document.getFullPath(), ex);
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException ex) {
+ //Ignore
+ }
+ }
+ }
+
+ // Use the final array and clear the original (we don't use it anymore)
+ mAdjustedLines = new ArrayList<String>(mLines);
+ mLines.clear();
+ }
+
+ }, attr);
+ }
+
+ /**
+ * Method that prints the document as an image
+ *
+ * @param ctx The current context
+ * @param fso The image to print
+ */
+ private static void printImage(final Context ctx, final FileSystemObject image) {
+ // Check that the image is supported by Android
+ if (isValidImageDocument(image.getFullPath())) {
+ DialogHelper.showToast(ctx, R.string.print_unsupported_image, Toast.LENGTH_SHORT);
+ return;
+ }
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.RGB_565;
+ final Bitmap bitmap = BitmapFactory.decodeFile(image.getFullPath(), options);
+
+ PrintManager printManager = (PrintManager) ctx.getSystemService(Context.PRINT_SERVICE);
+ PrintAttributes.MediaSize mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
+ if (bitmap.getWidth() > bitmap.getHeight()) {
+ mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
+ }
+ PrintAttributes attr = new PrintAttributes.Builder()
+ .setMediaSize(mediaSize)
+ .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+ .build();
+ printManager.print(image.getName(), new PrintDocumentAdapter() {
+ private PrintAttributes mAttributes;
+
+ @Override
+ public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+ CancellationSignal cancellationSignal, WriteResultCallback callback) {
+ PrintedPdfDocument pdfDocument = new PrintedPdfDocument(ctx,
+ mAttributes);
+ try {
+ Page page = pdfDocument.startPage(1);
+
+ RectF content = new RectF(page.getInfo().getContentRect());
+
+ Matrix matrix = getMatrix(bitmap.getWidth(), bitmap.getHeight(), content);
+
+ // Draw the bitmap.
+ page.getCanvas().drawBitmap(bitmap, matrix, null);
+
+ // Finish the page.
+ pdfDocument.finishPage(page);
+
+ try {
+ // Write the document
+ pdfDocument.writeTo(new FileOutputStream(destination.getFileDescriptor()));
+
+ // Done
+ callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
+ } catch (IOException ioe) {
+ // Failed.
+ ExceptionUtil.translateException(ctx, ioe);
+ callback.onWriteFailed(null);
+ }
+ } finally {
+ if (pdfDocument != null) {
+ pdfDocument.close();
+ }
+ if (destination != null) {
+ try {
+ destination.close();
+ } catch (IOException ioe) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+ CancellationSignal cancellationSignal, LayoutResultCallback callback,
+ Bundle extras) {
+
+ mAttributes = newAttributes;
+
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(image.getName())
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+ .setPageCount(1)
+ .build();
+ boolean changed = !newAttributes.equals(oldAttributes);
+ callback.onLayoutFinished(info, changed);
+ }
+
+ @Override
+ public void onFinish() {
+ super.onFinish();
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ }
+
+ private Matrix getMatrix(int imageWidth, int imageHeight, RectF content) {
+ Matrix matrix = new Matrix();
+
+ // Compute and apply scale to fill the page.
+ int widthRatio = content.width() / imageWidth;
+ int heightRatio = content.height() / imageHeight;
+ float scale = Math.max(widthRatio, heightRatio);
+ matrix.postScale(scale, scale);
+
+ // Center the content.
+ final float translateX = (content.width()
+ - imageWidth * scale) / 2;
+ final float translateY = (content.height()
+ - imageHeight * scale) / 2;
+ matrix.postTranslate(translateX, translateY);
+ return matrix;
+ }
+ }, attr);
+ }
+
+ /**
+ * Check if the file is a valid image document allowed by android to be printed
+ *
+ * @param file The image to check
+ * @return boolean If the image is a valid document
+ */
+ private static boolean isValidImageDocument(String file) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ options.inPreferredConfig = Bitmap.Config.RGB_565;
+ Bitmap bitmap = BitmapFactory.decodeFile(file, options);
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ return bitmap != null;
+ }
+} \ No newline at end of file
diff --git a/src/com/cyanogenmod/filemanager/util/StringHelper.java b/src/com/cyanogenmod/filemanager/util/StringHelper.java
new file mode 100644
index 00000000..3702746b
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/util/StringHelper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed 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 com.cyanogenmod.filemanager.util;
+
+import android.text.TextUtils;
+
+import com.android.internal.util.HexDump;
+
+import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ * A helper class with useful methods for deal with strings.
+ */
+public final class StringHelper {
+
+ private static final char[] VALID_NON_PRINTABLE_CHARS = {' ', '\t', '\r', '\n'};
+
+ /**
+ * Method that check if a character is valid printable character
+ *
+ * @param c The character to check
+ * @return boolean If the character is printable
+ */
+ public static boolean isPrintableCharacter(char c) {
+ int cc = VALID_NON_PRINTABLE_CHARS.length;
+ for (int i = 0; i < cc; i++) {
+ if (c == VALID_NON_PRINTABLE_CHARS[i]) {
+ return true;
+ }
+ }
+ return TextUtils.isGraphic(c);
+ }
+
+ /**
+ * Method that converts to a visual printable hex string
+ *
+ * @param string The string to check
+ */
+ public static String toHexPrintableString(byte[] data) {
+ String hexLineSeparator = UUID.randomUUID().toString() + UUID.randomUUID().toString();
+ String hex = toHexDump(data, hexLineSeparator);
+
+ // Remove characters without visual representation
+ final String REPLACED_SYMBOL = "."; //$NON-NLS-1$
+ final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+ String printable = hex.replaceAll("\\p{Cntrl}", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll("[^\\p{Print}]", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll("\\p{C}", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll(hexLineSeparator, NEWLINE);
+ return printable;
+ }
+
+ /**
+ * Create a hex dump of the data while show progress to user
+ *
+ * @param data The data to hex dump
+ * @param hexLineSeparator Internal line separator
+ * @return StringBuilder The hex dump buffer
+ */
+ private static String toHexDump(byte[] data, String hexLineSeparator) {
+ final int DISPLAY_SIZE = 16; // Bytes per line
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ byte[] line = new byte[DISPLAY_SIZE];
+ int read = 0;
+ int offset = 0;
+ StringBuilder sb = new StringBuilder();
+ while ((read = bais.read(line, 0, DISPLAY_SIZE)) != -1) {
+ //offset dump(16) data\n
+ String linedata = new String(line, 0, read);
+ sb.append(HexDump.toHexString(offset));
+ sb.append(" "); //$NON-NLS-1$
+ String hexDump = HexDump.toHexString(line, 0, read);
+ if (hexDump.length() != (DISPLAY_SIZE * 2)) {
+ char[] array = new char[(DISPLAY_SIZE * 2) - hexDump.length()];
+ Arrays.fill(array, ' ');
+ hexDump += new String(array);
+ }
+ sb.append(hexDump);
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(linedata);
+ sb.append(hexLineSeparator);
+ offset += DISPLAY_SIZE;
+ }
+
+ return sb.toString();
+ }
+} \ No newline at end of file