diff options
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 Binary files differnew file mode 100644 index 00000000..623b7150 --- /dev/null +++ b/res/drawable-hdpi/ic_holo_light_copy.png diff --git a/res/drawable-mdpi/ic_holo_light_copy.png b/res/drawable-mdpi/ic_holo_light_copy.png Binary files differnew file mode 100644 index 00000000..efb2445f --- /dev/null +++ b/res/drawable-mdpi/ic_holo_light_copy.png diff --git a/res/drawable-xhdpi/ic_holo_light_copy.png b/res/drawable-xhdpi/ic_holo_light_copy.png Binary files differnew file mode 100644 index 00000000..00bff33c --- /dev/null +++ b/res/drawable-xhdpi/ic_holo_light_copy.png 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 && /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 Binary files differnew file mode 100644 index 00000000..72c6bc6e --- /dev/null +++ b/themes/res/drawable-hdpi/ic_holo_dark_copy.png diff --git a/themes/res/drawable-mdpi/ic_holo_dark_copy.png b/themes/res/drawable-mdpi/ic_holo_dark_copy.png Binary files differnew file mode 100644 index 00000000..d93968e5 --- /dev/null +++ b/themes/res/drawable-mdpi/ic_holo_dark_copy.png diff --git a/themes/res/drawable-xhdpi/ic_holo_dark_copy.png b/themes/res/drawable-xhdpi/ic_holo_dark_copy.png Binary files differnew file mode 100644 index 00000000..04e290d8 --- /dev/null +++ b/themes/res/drawable-xhdpi/ic_holo_dark_copy.png 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> |