aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/drawable-hdpi/ic_holo_light_copy.pngbin0 -> 1373 bytes
-rw-r--r--res/drawable-mdpi/ic_holo_light_copy.pngbin0 -> 1321 bytes
-rw-r--r--res/drawable-xhdpi/ic_holo_light_copy.pngbin0 -> 1438 bytes
-rw-r--r--res/layout/compute_checksum_dialog.xml132
-rw-r--r--res/menu/actions.xml4
-rw-r--r--res/values/overlay.xml4
-rw-r--r--res/values/strings.xml17
-rw-r--r--res/values/theme.xml1
-rw-r--r--res/xml/command_list.xml1
-rw-r--r--src/com/cyanogenmod/filemanager/commands/ChecksumExecutable.java52
-rw-r--r--src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java15
-rw-r--r--src/com/cyanogenmod/filemanager/commands/java/ChecksumCommand.java271
-rw-r--r--src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java11
-rw-r--r--src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommand.java198
-rw-r--r--src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java18
-rw-r--r--src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java11
-rw-r--r--src/com/cyanogenmod/filemanager/ui/dialogs/ComputeChecksumDialog.java295
-rw-r--r--src/com/cyanogenmod/filemanager/ui/dialogs/ExecutionDialog.java40
-rw-r--r--src/com/cyanogenmod/filemanager/ui/policy/InfoActionPolicy.java14
-rw-r--r--src/com/cyanogenmod/filemanager/util/CommandHelper.java36
-rw-r--r--tests/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommandTest.java107
-rw-r--r--themes/res/drawable-hdpi/ic_holo_dark_copy.pngbin0 -> 1386 bytes
-rw-r--r--themes/res/drawable-mdpi/ic_holo_dark_copy.pngbin0 -> 1337 bytes
-rw-r--r--themes/res/drawable-xhdpi/ic_holo_dark_copy.pngbin0 -> 1426 bytes
-rw-r--r--themes/res/values/dark_theme.xml1
25 files changed, 1213 insertions, 15 deletions
diff --git a/res/drawable-hdpi/ic_holo_light_copy.png b/res/drawable-hdpi/ic_holo_light_copy.png
new file mode 100644
index 00000000..623b7150
--- /dev/null
+++ b/res/drawable-hdpi/ic_holo_light_copy.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_holo_light_copy.png b/res/drawable-mdpi/ic_holo_light_copy.png
new file mode 100644
index 00000000..efb2445f
--- /dev/null
+++ b/res/drawable-mdpi/ic_holo_light_copy.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_holo_light_copy.png b/res/drawable-xhdpi/ic_holo_light_copy.png
new file mode 100644
index 00000000..00bff33c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_holo_light_copy.png
Binary files differ
diff --git a/res/layout/compute_checksum_dialog.xml b/res/layout/compute_checksum_dialog.xml
new file mode 100644
index 00000000..c737b07b
--- /dev/null
+++ b/res/layout/compute_checksum_dialog.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/extra_large_margin"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/checksum_filename_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin"
+ android:layout_marginTop="@dimen/extra_large_margin"
+ android:gravity="left|center_vertical"
+ android:text="@string/compute_checksum_filename_label"
+ android:textAppearance="@style/primary_text_appearance" />
+
+ <TextView
+ android:id="@+id/checksum_filename"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin"
+ android:gravity="left|center_vertical"
+ android:textAppearance="@style/secondary_text_appearance" />
+
+ <TextView
+ android:id="@+id/checksum_md5_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin"
+ android:layout_marginTop="@dimen/extra_large_margin"
+ android:gravity="left|center_vertical"
+ android:text="@string/compute_checksum_md5_label"
+ android:textAppearance="@style/primary_text_appearance" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin">
+
+ <com.cyanogenmod.filemanager.ui.widgets.ButtonItem
+ android:id="@+id/bt_md5_clipboard"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:contentDescription="@string/copy_text_cd" />
+
+ <EditText
+ android:id="@+id/checksum_md5"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:singleLine="false"
+ android:gravity="top|left"
+ android:cursorVisible="true"
+ android:background="@color/console_bg"
+ android:imeOptions="actionNone|flagNoFullscreen"
+ android:inputType="textMultiLine|textImeMultiLine"
+ android:focusable="false"
+ android:layout_toLeftOf="@id/bt_md5_clipboard"
+ android:layout_alignTop="@id/bt_md5_clipboard"
+ android:layout_alignBottom="@id/bt_md5_clipboard"
+ android:layout_marginRight="@dimen/default_margin"
+ android:text="@string/compute_checksum_computing_checksum_msg"
+ android:textAppearance="@style/console_text_appearance" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/checksum_sha1_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin"
+ android:layout_marginTop="@dimen/extra_large_margin"
+ android:gravity="left|center_vertical"
+ android:text="@string/compute_checksum_sha1_label"
+ android:textAppearance="@style/primary_text_appearance" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/extra_large_margin"
+ android:layout_marginRight="@dimen/extra_large_margin"
+ android:layout_marginBottom="@dimen/extra_large_margin">
+
+ <com.cyanogenmod.filemanager.ui.widgets.ButtonItem
+ android:id="@+id/bt_sha1_clipboard"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:contentDescription="@string/copy_text_cd" />
+
+ <EditText
+ android:id="@+id/checksum_sha1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="false"
+ android:gravity="top|left"
+ android:cursorVisible="true"
+ android:background="@color/console_bg"
+ android:imeOptions="actionNone|flagNoFullscreen"
+ android:inputType="textMultiLine|textImeMultiLine"
+ android:focusable="false"
+ android:layout_toLeftOf="@id/bt_sha1_clipboard"
+ android:layout_alignTop="@id/bt_sha1_clipboard"
+ android:layout_alignBottom="@id/bt_sha1_clipboard"
+ android:layout_marginRight="@dimen/default_margin"
+ android:text="@string/compute_checksum_computing_checksum_msg"
+ android:textAppearance="@style/console_text_appearance" />
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index 44982860..305c6bba 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -139,6 +139,10 @@
android:showAsAction="ifRoom"
android:title="@string/actions_menu_add_shortcut"/>
<item
+ android:id="@+id/mnu_actions_compute_checksum"
+ android:showAsAction="ifRoom"
+ android:title="@string/actions_menu_compute_checksum"/>
+ <item
android:id="@+id/mnu_actions_open_parent_folder"
android:showAsAction="ifRoom"
android:title="@string/actions_menu_open_parent_folder"/>
diff --git a/res/values/overlay.xml b/res/values/overlay.xml
index 63ce69f7..dcea6e6d 100644
--- a/res/values/overlay.xml
+++ b/res/values/overlay.xml
@@ -68,7 +68,9 @@
/system/xbin/unlzma,
/system/xbin/unxz,
/system/xbin/unzip,
- /system/xbin/xargs
+ /system/xbin/xargs,
+ /system/xbin/md5sum,
+ /system/xbin/sha1sum
</string>
<!-- The mounts file -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9a15cfce..c95af3d7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -62,6 +62,10 @@
<string name="cancelled_message">Cancelled.</string>
<!-- Error message -->
<string name="error_message">Error.</string>
+ <!-- Copy text content description -->
+ <string name="copy_text_cd">Tap to copy text to clipboard</string>
+ <!-- Copy text content message -->
+ <string name="copy_text_msg">Text copied to clipboard</string>
<!-- Warning dialog title -->
<string name="warning_title">Warning</string>
@@ -522,6 +526,8 @@
<string name="actions_menu_add_shortcut">Add shortcut</string>
<!-- Actions Dialog * Menu * Open parent folder -->
<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 * Ask user prior to do an undone operation. Dialog message -->
<string name="actions_ask_undone_operation_msg">
@@ -568,6 +574,17 @@
<string name="execution_console_script_execution_time_text">
<xliff:g id="seconds">%1$s</xliff:g> sec.</string>
+ <!-- Compute checksum * Title -->
+ <string name="compute_checksum_title">Compute checksum</string>
+ <!-- Compute checksum * The file name label -->
+ <string name="compute_checksum_filename_label">File:</string>
+ <!-- Compute checksum * The MD5 label -->
+ <string name="compute_checksum_md5_label" translatable="false">MD5:</string>
+ <!-- Compute checksum * The SHA1 label -->
+ <string name="compute_checksum_sha1_label" translatable="false">SHA-1:</string>
+ <!-- Compute checksum * The computing checksum message-->
+ <string name="compute_checksum_computing_checksum_msg">Computing checksum\u2026</string>
+
<!-- Mime/Types * Folder -->
<string name="mime_folder">Folder</string>
<!-- Mime/Types * Symlink -->
diff --git a/res/values/theme.xml b/res/values/theme.xml
index ae16b76e..629b234c 100644
--- a/res/values/theme.xml
+++ b/res/values/theme.xml
@@ -124,6 +124,7 @@
<drawable name="ic_usb_drawable">@drawable/ic_holo_light_usb</drawable>
<drawable name="ic_user_defined_bookmark_drawable">@drawable/ic_holo_light_user_defined_bookmark</drawable>
<drawable name="ic_history_search_drawable">@drawable/ic_holo_light_history_search</drawable>
+ <drawable name="ic_copy_drawable">@drawable/ic_holo_light_copy</drawable>
<!-- Disk usage graph -->
<color name="disk_usage_total_color">@color/disk_usage_total</color>
diff --git a/res/xml/command_list.xml b/res/xml/command_list.xml
index 3138195d..1d61a675 100644
--- a/res/xml/command_list.xml
+++ b/res/xml/command_list.xml
@@ -80,6 +80,7 @@
<!-- Misc -->
<command commandId="dirname" commandPath="/system/xbin/dirname" commandArgs="%1$s" />
<command commandId="echo" commandPath="/system/xbin/echo" commandArgs="%1$s" />
+ <command commandId="checksum" commandPath="/system/xbin/md5sum" commandArgs="%1$s &amp;&amp; /system/xbin/sha1sum %1$s" />
<!-- Process control and info -->
<command commandId="pid_shell" commandPath="/system/xbin/echo" commandArgs="$$" />
diff --git a/src/com/cyanogenmod/filemanager/commands/ChecksumExecutable.java b/src/com/cyanogenmod/filemanager/commands/ChecksumExecutable.java
new file mode 100644
index 00000000..43d518d0
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/commands/ChecksumExecutable.java
@@ -0,0 +1,52 @@
+/*
+ * 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.commands;
+
+/**
+ * An interface that represents an executable for calculate checksum of file system objects.
+ */
+public interface ChecksumExecutable extends AsyncResultExecutable {
+
+ /**
+ * Checksum enumerations
+ */
+ public enum CHECKSUMS {
+ /**
+ * MD5 digest algorithm
+ */
+ MD5,
+ /**
+ * SHA-1 digest algorithm
+ */
+ SHA1
+ }
+
+ /**
+ * Method that returns the calculated MD5 [0] and SHA-1 [1] digests
+ *
+ * @return String[] The calculated MD5 [0] and SHA-1 [1] digests
+ */
+ String[] getResult();
+
+ /**
+ * Method that returns a calculated digest checksum
+ *
+ * @param checksum The checksum to return
+ * @return String The calculated digest to return
+ */
+ String getChecksum(CHECKSUMS checksum);
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java b/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
index ea7b958d..f5c2f169 100644
--- a/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
+++ b/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
@@ -518,4 +518,19 @@ public interface ExecutableCreator {
throws CommandNotFoundException,
NoSuchFileOrDirectory, InsufficientPermissionsException;
+ /**
+ * Method that creates an executable for calculate checksums of file system objects.
+ *
+ * @param src The compressed file
+ * @param asyncResultListener The listener where to return partial results
+ * @return ChecksumExecutable A {@link ChecksumExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ */
+ ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException;
+
}
diff --git a/src/com/cyanogenmod/filemanager/commands/java/ChecksumCommand.java b/src/com/cyanogenmod/filemanager/commands/java/ChecksumCommand.java
new file mode 100644
index 00000000..d18c557f
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/commands/java/ChecksumCommand.java
@@ -0,0 +1,271 @@
+/*
+ * 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.commands.java;
+
+import android.util.Log;
+
+import com.android.internal.util.HexDump;
+import com.cyanogenmod.filemanager.commands.AsyncResultListener;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
+import com.cyanogenmod.filemanager.console.ExecutionException;
+import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+
+/**
+ * A class for calculate MD5 and SHA-1 checksums of a file system object.<br />
+ * <br />
+ * Partial results are returned in order (MD5 -> SHA1)
+ */
+public class ChecksumCommand extends Program implements ChecksumExecutable {
+
+ private static final String TAG = "ChecksumCommand"; //$NON-NLS-1$
+
+ private final File mSrc;
+ private final String[] mChecksums;
+ private final AsyncResultListener mAsyncResultListener;
+
+ private boolean mCancelled;
+ private final Object mSync = new Object();
+
+ /**
+ * Constructor of <code>ChecksumCommand</code>.
+ *
+ * @param src The source file
+ * @param asyncResultListener The partial result listener
+ */
+ public ChecksumCommand(
+ String src, AsyncResultListener asyncResultListener) {
+ super();
+ this.mAsyncResultListener = asyncResultListener;
+ this.mChecksums = new String[]{null, null};
+ this.mSrc = new File(src);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute() throws InsufficientPermissionsException,
+ NoSuchFileOrDirectory, ExecutionException {
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Calculating checksums of file %s", this.mSrc)); //$NON-NLS-1$
+ }
+
+ // Check that the file exists
+ if (!this.mSrc.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mSrc.getAbsolutePath());
+ }
+
+ CHECKSUMS checksum = CHECKSUMS.MD5;
+ try {
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ // Calculate digests
+ calculateDigest(checksum);
+ checksum = CHECKSUMS.SHA1;
+ calculateDigest(checksum);
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(false);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+
+ } catch (InterruptedException ie) {
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(true);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(143);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: CANCELLED"); //$NON-NLS-1$
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG,
+ String.format(
+ "Fail to calculate %s checksum of file %s", //$NON-NLS-1$
+ checksum.name(),
+ this.mSrc.getAbsolutePath()),
+ e);
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(e);
+ }
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ try {
+ synchronized (this.mSync) {
+ this.mCancelled = true;
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ return cancel();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getResult() {
+ return this.mChecksums;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChecksum(CHECKSUMS checksum) {
+ return getResult()[checksum.ordinal()];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+
+ /**
+ * Method that calculate a digest of the file for the source file
+ *
+ * @param type The type of digest to obtain
+ * @throws InterruptedException If the operation was cancelled
+ * @throws Exception If an error occurs
+ */
+ private void calculateDigest(CHECKSUMS type) throws InterruptedException, Exception {
+
+ InputStream is = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance(type.name());
+ is = new FileInputStream(this.mSrc);
+
+ // Start digesting
+ byte[] data = new byte[getBufferSize()];
+ int read = 0;
+ while ((read = is.read(data, 0, getBufferSize())) != -1) {
+ checkCancelled();
+ md.update(data, 0, read);
+ }
+ checkCancelled();
+
+ // Finally digest
+ this.mChecksums[type.ordinal()] = HexDump.toHexString(md.digest());
+ checkCancelled();
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(this.mCancelled);
+ }
+
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * Checks if the operation was cancelled
+ *
+ * @throws InterruptedException If the operation was cancelled
+ */
+ private void checkCancelled() throws InterruptedException {
+ synchronized (this.mSync) {
+ if (this.mCancelled) {
+ throw new InterruptedException();
+ }
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java b/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
index 1f680e41..94856ba9 100644
--- a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
+++ b/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
@@ -20,6 +20,7 @@ import com.cyanogenmod.filemanager.R;
import com.cyanogenmod.filemanager.commands.AsyncResultListener;
import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
import com.cyanogenmod.filemanager.commands.CompressExecutable;
import com.cyanogenmod.filemanager.commands.CopyExecutable;
import com.cyanogenmod.filemanager.commands.CreateDirExecutable;
@@ -390,4 +391,14 @@ public class JavaExecutableCreator implements ExecutableCreator {
throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException {
+ return new ChecksumCommand(src, asyncResultListener);
+ }
+
}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommand.java b/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommand.java
new file mode 100644
index 00000000..97fbea39
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommand.java
@@ -0,0 +1,198 @@
+/*
+ * 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.commands.shell;
+
+import com.cyanogenmod.filemanager.commands.AsyncResultListener;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
+import com.cyanogenmod.filemanager.commands.SIGNAL;
+import com.cyanogenmod.filemanager.console.CommandNotFoundException;
+import com.cyanogenmod.filemanager.console.ExecutionException;
+import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+
+import java.io.File;
+
+/**
+ * A class for calculate MD5 and SHA-1 checksums of a file system object.<br />
+ * <br />
+ * Partial results are returned in order (MD5 -> SHA1)
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?md5sum"}
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?sha1sum"}
+ * @see com.cyanogenmod.filemanager.commands.ChecksumExecutable.CHECKSUMS
+ */
+public class ChecksumCommand extends AsyncResultProgram implements ChecksumExecutable {
+
+ private static final String ID = "checksum"; //$NON-NLS-1$
+
+ private final String mName;
+ private final String[] mChecksums;
+ private int mChecksumsCounter;
+ private String mPartial;
+
+ /**
+ * Constructor of <code>ChecksumCommand</code>.
+ *
+ * @param src The source file
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ChecksumCommand(String src, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, src);
+ this.mChecksums = new String[]{null, null};
+ this.mName = new File(src).getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ this.mChecksums[0] = null;
+ this.mChecksums[1] = null;
+ this.mChecksumsCounter = 0;
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {
+ // Send the last partial data
+ if (this.mPartial != null && this.mPartial.length() > 0) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(this.mPartial);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+ if (partialIn == null || partialIn.length() ==0) return;
+ boolean endsWithNewLine = partialIn.endsWith("\n"); //$NON-NLS-1$
+ String[] lines = partialIn.split("\n"); //$NON-NLS-1$
+
+ // Append the pending data to the first line
+ lines[0] = this.mPartial + lines[0];
+
+ // Return all the lines, except the last
+ for (int i = 0; i < lines.length-1; i++) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[i]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+
+ // Return the last line?
+ if (endsWithNewLine) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[lines.length-1]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ } else {
+ // Save the partial for next calls
+ this.mPartial = lines[lines.length-1];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ try {
+ if (this.getProgramListener().getOutputStream() != null) {
+ this.getProgramListener().getOutputStream().flush();
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ try {
+ Thread.yield();
+ } catch (Exception ex) {/**NON BLOCK**/}
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getResult() {
+ return this.mChecksums;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChecksum(CHECKSUMS checksum) {
+ return getResult()[checksum.ordinal()];
+ }
+
+ /**
+ * Method that processes a line to determine if it's a valid partial result
+ *
+ * @param line The line to process
+ * @return String The processed line
+ */
+ private String processPartialResult(String line) {
+ // MD5 and SHA-1 return both the digest and the name of the file
+ // 4c044b884cf2ff3839713da0e81dced19f099b09 boot.zip
+ int pos = line.indexOf(" "); //$NON-NLS-1$
+ if (line.endsWith(this.mName) && pos != -1) {
+ String digest = line.substring(0, pos).trim();
+ if (this.mChecksumsCounter < this.mChecksums.length) {
+ this.mChecksums[this.mChecksumsCounter] = digest;
+ }
+ this.mChecksumsCounter++;
+ return digest;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ //Ignore exit code 143 (cancelled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java b/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
index 339ca09f..ca304f5f 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
+++ b/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
@@ -19,6 +19,7 @@ package com.cyanogenmod.filemanager.commands.shell;
import com.cyanogenmod.filemanager.commands.AsyncResultListener;
import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
import com.cyanogenmod.filemanager.commands.CompressExecutable;
import com.cyanogenmod.filemanager.commands.CopyExecutable;
import com.cyanogenmod.filemanager.commands.CreateDirExecutable;
@@ -48,6 +49,8 @@ import com.cyanogenmod.filemanager.commands.SendSignalExecutable;
import com.cyanogenmod.filemanager.commands.UncompressExecutable;
import com.cyanogenmod.filemanager.commands.WriteExecutable;
import com.cyanogenmod.filemanager.console.CommandNotFoundException;
+import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
import com.cyanogenmod.filemanager.console.shell.ShellConsole;
import com.cyanogenmod.filemanager.model.Group;
import com.cyanogenmod.filemanager.model.MountPoint;
@@ -520,4 +523,19 @@ public class ShellExecutableCreator implements ExecutableCreator {
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException, NoSuchFileOrDirectory,
+ InsufficientPermissionsException {
+ try {
+ return new ChecksumCommand(src, asyncResultListener);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("ChecksumCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
}
diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
index efb66271..8c205434 100644
--- a/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
+++ b/src/com/cyanogenmod/filemanager/ui/dialogs/ActionsDialog.java
@@ -38,6 +38,7 @@ import com.cyanogenmod.filemanager.adapters.TwoColumnsMenuListAdapter;
import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
import com.cyanogenmod.filemanager.listeners.OnSelectionListener;
import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.model.Symlink;
import com.cyanogenmod.filemanager.model.SystemFile;
import com.cyanogenmod.filemanager.preferences.AccessMode;
import com.cyanogenmod.filemanager.ui.ThemeManager;
@@ -394,6 +395,11 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
IntentsActionPolicy.createShortcut(this.mContext, this.mFso);
break;
+ //- Compute checksum
+ case R.id.mnu_actions_compute_checksum:
+ InfoActionPolicy.showComputeChecksumDialog(this.mContext, this.mFso);
+ break;
+
//- Properties
case R.id.mnu_actions_properties:
case R.id.mnu_actions_properties_current_folder:
@@ -614,6 +620,11 @@ public class ActionsDialog implements OnItemClickListener, OnItemLongClickListen
if (category.compareTo(MimeTypeCategory.EXEC) != 0) {
menu.removeItem(R.id.mnu_actions_execute);
}
+
+ //- Checksum (only supported for files)
+ if (FileHelper.isDirectory(this.mFso) || this.mFso instanceof Symlink) {
+ menu.removeItem(R.id.mnu_actions_compute_checksum);
+ }
}
//- Add to bookmarks -> Only directories
diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/ComputeChecksumDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/ComputeChecksumDialog.java
new file mode 100644
index 00000000..f7b34bb7
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/ui/dialogs/ComputeChecksumDialog.java
@@ -0,0 +1,295 @@
+/*
+ * 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.dialogs;
+
+import android.app.AlertDialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
+import com.cyanogenmod.filemanager.commands.AsyncResultListener;
+import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.ui.ThemeManager;
+import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import com.cyanogenmod.filemanager.util.CommandHelper;
+import com.cyanogenmod.filemanager.util.DialogHelper;
+import com.cyanogenmod.filemanager.util.ExceptionUtil;
+/**
+ * A class that wraps a dialog for computing the checksums of a {@link FileSystemObject}
+ */
+public class ComputeChecksumDialog implements
+ DialogInterface.OnClickListener, View.OnClickListener, AsyncResultListener {
+
+ /**
+ * @hide
+ */
+ final Context mContext;
+ /**
+ * @hide
+ */
+ final FileSystemObject mFso;
+ private final Handler mHandler;
+ /**
+ * @hide
+ */
+ final AlertDialog mDialog;
+
+ // For cancel the operation
+ /**
+ * @hide
+ */
+ AsyncResultExecutable mCmd;
+ /**
+ * @hide
+ */
+ boolean mFinished;
+
+ /**
+ * @hide
+ */
+ EditText[] mChecksums = new EditText[2];
+
+ /**
+ * @hide
+ */
+ int mComputeStatus;
+
+ private final ClipboardManager mClipboardMgr;
+
+ /**
+ * Constructor of <code>ComputeChecksumDialog</code>.
+ *
+ * @param context The current context
+ * @param fso The file system object to execute
+ */
+ public ComputeChecksumDialog(final Context context, final FileSystemObject fso) {
+ super();
+
+ // Save properties
+ this.mContext = context;
+ this.mFso = fso;
+ this.mHandler = new Handler();
+ this.mComputeStatus = 0;
+
+ this.mClipboardMgr =
+ (ClipboardManager)this.mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+
+ //Create the layout
+ LayoutInflater li =
+ (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ViewGroup layout = (ViewGroup)li.inflate(R.layout.compute_checksum_dialog, null);
+ TextView tvFileName = (TextView)layout.findViewById(R.id.checksum_filename);
+ tvFileName.setText(fso.getFullPath());
+ this.mChecksums[0] = (EditText)layout.findViewById(R.id.checksum_md5);
+ this.mChecksums[1] = (EditText)layout.findViewById(R.id.checksum_sha1);
+ View btMD5 = layout.findViewById(R.id.bt_md5_clipboard);
+ btMD5.setOnClickListener(this);
+ View btSHA1 = layout.findViewById(R.id.bt_sha1_clipboard);
+ btSHA1.setOnClickListener(this);
+
+ // Apply the theme
+ applyTheme(context, layout);
+
+ //Create the dialog
+ String title = context.getString(R.string.compute_checksum_title);
+ this.mDialog = DialogHelper.createDialog(
+ context,
+ 0,
+ title,
+ layout);
+ this.mDialog.setButton(
+ DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this);
+
+ // Start checksum compute
+ try {
+ this.mCmd = CommandHelper.checksum(context, fso.getFullPath(), this, null);
+ } catch (Exception e) {
+ ExceptionUtil.translateException(context, e);
+ }
+ }
+
+ /**
+ * Method that shows the dialog.
+ */
+ public void show() {
+ DialogHelper.delegateDialogShow(this.mContext, this.mDialog);
+ }
+
+ /**
+ * Method that dismiss the dialog.
+ */
+ public void dismiss() {
+ this.mDialog.dismiss();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_NEUTRAL:
+ // Cancel the program?
+ try {
+ if (this.mCmd != null && !this.mFinished) {
+ if (this.mCmd.isCancellable() && !this.mCmd.isCancelled()) {
+ this.mCmd.cancel();
+ }
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ this.mDialog.dismiss();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onClick(View v) {
+ String digest = ""; //$NON-NLS-1$
+ String label = ""; //$NON-NLS-1$
+ switch (v.getId()) {
+ case R.id.bt_md5_clipboard:
+ digest = this.mChecksums[0].getText().toString();
+ label = String.format("MD5 Checksum - %s", this.mFso.getFullPath()); //$NON-NLS-1$
+ break;
+ case R.id.bt_sha1_clipboard:
+ digest = this.mChecksums[1].getText().toString();
+ label = String.format("SHA-1 Checksum - %s", this.mFso.getFullPath()); //$NON-NLS-1$
+ break;
+
+ default:
+ break;
+ }
+
+ // Copy text to clipboard
+ if (this.mClipboardMgr != null) {
+ ClipData clip =ClipData.newPlainText(label, digest);
+ this.mClipboardMgr.setPrimaryClip(clip);
+ DialogHelper.showToast(this.mContext, R.string.copy_text_msg, Toast.LENGTH_SHORT);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncStart() {
+ /** NON BLOCK **/
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncEnd(boolean cancelled) {
+ /** NON BLOCK **/
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncExitCode(int exitCode) {
+ if (exitCode != 0) {
+ this.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ int cc = ComputeChecksumDialog.this.mChecksums.length;
+ for (int i = ComputeChecksumDialog.this.mComputeStatus; i < cc; i++) {
+ ComputeChecksumDialog.this.mChecksums[i].setText(R.string.error_message);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPartialResult(final Object result) {
+ this.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ setChecksum(String.valueOf(result));
+ }
+ });
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onException(Exception cause) {
+ ExceptionUtil.translateException(this.mContext, cause, false, false);
+ }
+
+ /**
+ * Method that attach the checksum result to the view
+ *
+ * @param digest The digest value
+ * @hide
+ */
+ synchronized void setChecksum(String digest) {
+ this.mChecksums[this.mComputeStatus].setText(digest);
+ this.mComputeStatus++;
+ }
+
+ /**
+ * Method that applies the current theme to the dialog
+ *
+ * @param ctx The current context
+ * @param root The root view
+ */
+ private void applyTheme(Context ctx, ViewGroup root) {
+ // Apply the current theme
+ Theme theme = ThemeManager.getCurrentTheme(ctx);
+ theme.setBackgroundDrawable(ctx, root, "background_drawable"); //$NON-NLS-1$
+ View v = root.findViewById(R.id.checksum_filename_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.checksum_filename);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.checksum_md5_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ theme.setBackgroundColor(ctx, this.mChecksums[0], "console_bg_color"); //$NON-NLS-1$
+ theme.setTextColor(ctx, this.mChecksums[0], "console_fg_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.checksum_sha1_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ theme.setBackgroundColor(ctx, this.mChecksums[1], "console_bg_color"); //$NON-NLS-1$
+ theme.setTextColor(ctx, this.mChecksums[1], "console_fg_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.bt_md5_clipboard);
+ theme.setImageDrawable(ctx, (ImageView)v, "ic_copy_drawable"); //$NON-NLS-1$
+ v = root.findViewById(R.id.bt_sha1_clipboard);
+ theme.setImageDrawable(ctx, (ImageView)v, "ic_copy_drawable"); //$NON-NLS-1$
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/ui/dialogs/ExecutionDialog.java b/src/com/cyanogenmod/filemanager/ui/dialogs/ExecutionDialog.java
index 5bc8ed48..b0a7c3ae 100644
--- a/src/com/cyanogenmod/filemanager/ui/dialogs/ExecutionDialog.java
+++ b/src/com/cyanogenmod/filemanager/ui/dialogs/ExecutionDialog.java
@@ -146,29 +146,17 @@ public class ExecutionDialog implements DialogInterface.OnClickListener {
LayoutInflater li =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup layout = (ViewGroup)li.inflate(R.layout.execution_dialog, null);
- View tvScriptNameLabel = layout.findViewById(R.id.execution_script_name_label);
TextView tvScriptName = (TextView)layout.findViewById(R.id.execution_script_name);
tvScriptName.setText(fso.getFullPath());
- View tvTimeLabel = layout.findViewById(R.id.execution_time_label);
this.mTvTime = (TextView)layout.findViewById(R.id.execution_time);
this.mTvTime.setText("-"); //$NON-NLS-1$
- View tvExitCodeLabel = layout.findViewById(R.id.execution_exitcode_label);
this.mTvExitCode = (TextView)layout.findViewById(R.id.execution_exitcode);
this.mTvExitCode.setText("-"); //$NON-NLS-1$
this.mTvOutput = (TextView)layout.findViewById(R.id.execution_output);
this.mTvOutput.setMovementMethod(new ScrollingMovementMethod());
- // Apply the current theme
- Theme theme = ThemeManager.getCurrentTheme(context);
- theme.setBackgroundDrawable(context, layout, "background_drawable"); //$NON-NLS-1$
- theme.setTextColor(context, (TextView)tvScriptNameLabel, "text_color"); //$NON-NLS-1$
- theme.setTextColor(context, tvScriptName, "text_color"); //$NON-NLS-1$
- theme.setTextColor(context, (TextView)tvTimeLabel, "text_color"); //$NON-NLS-1$
- theme.setTextColor(context, this.mTvTime, "text_color"); //$NON-NLS-1$
- theme.setTextColor(context, (TextView)tvExitCodeLabel, "text_color"); //$NON-NLS-1$
- theme.setTextColor(context, this.mTvExitCode, "text_color"); //$NON-NLS-1$
- theme.setBackgroundColor(context, this.mTvOutput, "console_bg_color"); //$NON-NLS-1$
- theme.setTextColor(context, this.mTvOutput, "console_fg_color"); //$NON-NLS-1$
+ // Apply the theme
+ applyTheme(context, layout);
//Create the dialog
String title = context.getString(R.string.execution_console_title);
@@ -369,4 +357,28 @@ public class ExecutionDialog implements DialogInterface.OnClickListener {
}
}
+ /**
+ * Method that applies the current theme to the dialog
+ *
+ * @param ctx The current context
+ * @param root The root view
+ */
+ private void applyTheme(Context ctx, ViewGroup root) {
+ // Apply the current theme
+ Theme theme = ThemeManager.getCurrentTheme(ctx);
+ theme.setBackgroundDrawable(ctx, root, "background_drawable"); //$NON-NLS-1$
+ View v = root.findViewById(R.id.execution_time_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.execution_script_name);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.execution_time_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ theme.setTextColor(ctx, this.mTvTime, "text_color"); //$NON-NLS-1$
+ v = root.findViewById(R.id.execution_exitcode_label);
+ theme.setTextColor(ctx, (TextView)v, "text_color"); //$NON-NLS-1$
+ theme.setTextColor(ctx, this.mTvExitCode, "text_color"); //$NON-NLS-1$
+ theme.setBackgroundColor(ctx, this.mTvOutput, "console_bg_color"); //$NON-NLS-1$
+ theme.setTextColor(ctx, this.mTvOutput, "console_fg_color"); //$NON-NLS-1$
+ }
+
}
diff --git a/src/com/cyanogenmod/filemanager/ui/policy/InfoActionPolicy.java b/src/com/cyanogenmod/filemanager/ui/policy/InfoActionPolicy.java
index ef4639b0..d35eddb2 100644
--- a/src/com/cyanogenmod/filemanager/ui/policy/InfoActionPolicy.java
+++ b/src/com/cyanogenmod/filemanager/ui/policy/InfoActionPolicy.java
@@ -22,6 +22,7 @@ import android.widget.Toast;
import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
import com.cyanogenmod.filemanager.model.FileSystemObject;
+import com.cyanogenmod.filemanager.ui.dialogs.ComputeChecksumDialog;
import com.cyanogenmod.filemanager.ui.dialogs.FsoPropertiesDialog;
import com.cyanogenmod.filemanager.util.DialogHelper;
@@ -68,4 +69,17 @@ public final class InfoActionPolicy extends ActionsPolicy {
dialog.show();
}
+ /**
+ * Method that show a new dialog for compute checksum of a {@link FileSystemObject}.
+ *
+ * @param ctx The current context
+ * @param fso The file system object
+ * of the {@link FileSystemObject} were changed (optional)
+ */
+ public static void showComputeChecksumDialog(
+ final Context ctx, final FileSystemObject fso) {
+ //Show a the filesystem info dialog
+ final ComputeChecksumDialog dialog = new ComputeChecksumDialog(ctx, fso);
+ dialog.show();
+ }
} \ No newline at end of file
diff --git a/src/com/cyanogenmod/filemanager/util/CommandHelper.java b/src/com/cyanogenmod/filemanager/util/CommandHelper.java
index 85b6a036..2a5925c0 100644
--- a/src/com/cyanogenmod/filemanager/util/CommandHelper.java
+++ b/src/com/cyanogenmod/filemanager/util/CommandHelper.java
@@ -21,6 +21,7 @@ import android.content.Context;
import com.cyanogenmod.filemanager.commands.AsyncResultListener;
import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
import com.cyanogenmod.filemanager.commands.CompressExecutable;
import com.cyanogenmod.filemanager.commands.CopyExecutable;
import com.cyanogenmod.filemanager.commands.CreateDirExecutable;
@@ -1435,6 +1436,41 @@ public final class CommandHelper {
}
/**
+ * Method that calculates the checksum of a file system object.
+ *
+ * @param context The current context (needed if console == null)
+ * @param src The source file
+ * @param asyncResultListener The partial result listener
+ * @param console The console in which execute the program.
+ * <code>null</code> to attach to the default console
+ * @return WriteExecutable The command executed in background
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory couldn't be checked
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws ConsoleAllocException If the console can't be allocated
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws CommandNotFoundException If the command was not found
+ * @throws OperationTimeoutException If the operation exceeded the maximum time of wait
+ * @throws ExecutionException If the operation returns a invalid exit code
+ * @see WriteExecutable
+ */
+ public static ChecksumExecutable checksum(
+ Context context, String src,
+ AsyncResultListener asyncResultListener, Console console)
+ throws FileNotFoundException, IOException, ConsoleAllocException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException,
+ CommandNotFoundException, OperationTimeoutException,
+ ExecutionException, InvalidCommandDefinitionException {
+ Console c = ensureConsole(context, console);
+ ChecksumExecutable executable =
+ c.getExecutableFactory().newCreator().
+ createChecksumExecutable(src, asyncResultListener);
+ execute(context, executable, c);
+ return executable;
+ }
+
+ /**
* Method that re-execute the command.
*
* @param context The current context (needed if console == null)
diff --git a/tests/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommandTest.java b/tests/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommandTest.java
new file mode 100644
index 00000000..791a00e2
--- /dev/null
+++ b/tests/src/com/cyanogenmod/filemanager/commands/shell/ChecksumCommandTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.commands.shell;
+
+import android.os.Environment;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.cyanogenmod.filemanager.commands.AsyncResultListener;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable;
+import com.cyanogenmod.filemanager.commands.ChecksumExecutable.CHECKSUMS;
+import com.cyanogenmod.filemanager.util.CommandHelper;
+
+/**
+ * A class for testing checksum command.
+ *
+ * @see ChecksumCommand
+ */
+public class ChecksumCommandTest extends AbstractConsoleTest {
+
+ private static final String TEST_FILE =
+ Environment.getRootDirectory().getAbsolutePath() + "/fonts/Roboto-Bold.ttf"; //$NON-NLS-1$
+
+ private static final String MD5_SUM = "0a15e86bdff7da5886fe6535b50d9988"; //$NON-NLS-1$
+ private static final String SHA1_SUM = "624735f02422f13e50ccf466f0d29edda05adb36"; //$NON-NLS-1$
+
+ /**
+ * @hide
+ */
+ final Object mSync = new Object();
+ /**
+ * @hide
+ */
+ boolean mNormalEnd;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRootConsoleNeeded() {
+ return true;
+ }
+
+ /**
+ * Method that performs a checksum test
+ *
+ * @throws Exception If an exception occurs while executing the test
+ */
+ @SmallTest
+ @SuppressWarnings("null")
+ public void testChecksums() throws Exception {
+ ChecksumExecutable cmd =
+ CommandHelper.checksum(getContext(),
+ TEST_FILE, new AsyncResultListener() {
+ @Override
+ public void onAsyncStart() {
+ /**NON BLOCK**/
+ }
+ @Override
+ public void onAsyncEnd(boolean cancelled) {
+ synchronized (ChecksumCommandTest.this.mSync) {
+ ChecksumCommandTest.this.mNormalEnd = true;
+ ChecksumCommandTest.this.mSync.notify();
+ }
+ }
+ @Override
+ public void onAsyncExitCode(int exitCode) {
+ /**NON BLOCK**/
+ }
+ @Override
+ public void onException(Exception cause) {
+ fail(String.valueOf(cause));
+ }
+ @Override
+ public void onPartialResult(Object results) {
+ /**NON BLOCK**/
+ }
+ }, getConsole());
+
+ synchronized (ChecksumCommandTest.this.mSync) {
+ ChecksumCommandTest.this.mSync.wait(15000L);
+ }
+ try {
+ if (!this.mNormalEnd && cmd != null && cmd.isCancellable() && !cmd.isCancelled()) {
+ cmd.cancel();
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ assertNotNull("md5==null", cmd.getChecksum(CHECKSUMS.MD5)); //$NON-NLS-1$
+ assertNotNull("sha1==null", cmd.getChecksum(CHECKSUMS.SHA1)); //$NON-NLS-1$
+ assertEquals("md5sum fails", MD5_SUM, cmd.getChecksum(CHECKSUMS.MD5)); //$NON-NLS-1$
+ assertEquals("sha1sum fails", SHA1_SUM, cmd.getChecksum(CHECKSUMS.SHA1)); //$NON-NLS-1$
+ }
+
+}
diff --git a/themes/res/drawable-hdpi/ic_holo_dark_copy.png b/themes/res/drawable-hdpi/ic_holo_dark_copy.png
new file mode 100644
index 00000000..72c6bc6e
--- /dev/null
+++ b/themes/res/drawable-hdpi/ic_holo_dark_copy.png
Binary files differ
diff --git a/themes/res/drawable-mdpi/ic_holo_dark_copy.png b/themes/res/drawable-mdpi/ic_holo_dark_copy.png
new file mode 100644
index 00000000..d93968e5
--- /dev/null
+++ b/themes/res/drawable-mdpi/ic_holo_dark_copy.png
Binary files differ
diff --git a/themes/res/drawable-xhdpi/ic_holo_dark_copy.png b/themes/res/drawable-xhdpi/ic_holo_dark_copy.png
new file mode 100644
index 00000000..04e290d8
--- /dev/null
+++ b/themes/res/drawable-xhdpi/ic_holo_dark_copy.png
Binary files differ
diff --git a/themes/res/values/dark_theme.xml b/themes/res/values/dark_theme.xml
index c67474e4..f1944d84 100644
--- a/themes/res/values/dark_theme.xml
+++ b/themes/res/values/dark_theme.xml
@@ -118,6 +118,7 @@
<drawable name="dark_ic_usb_drawable">@drawable/ic_holo_dark_usb</drawable>
<drawable name="dark_ic_user_defined_bookmark_drawable">@drawable/ic_holo_dark_user_defined_bookmark</drawable>
<!--<drawable name="dark_ic_history_search_drawable">@null</drawable> -->
+ <drawable name="dark_ic_copy_drawable">@drawable/ic_holo_dark_copy</drawable>
<!-- Disk usage graph -->
<color name="dark_disk_usage_total_color">#7ecccccc</color>