OSDN Git Service

Fix commands
authorjruesga <jorge@ruesga.com>
Sat, 29 Sep 2012 23:39:06 +0000 (01:39 +0200)
committerjruesga <jorge@ruesga.com>
Sat, 29 Sep 2012 23:39:06 +0000 (01:39 +0200)
* Fix cancelable commands
* New pid_shell and pid_cmd command
* New FolderUsage command
* New compute folder statistics in fso properties
* Add overlay file for device specific stuff
* Fix invalid executable drawable
* Clean up

27 files changed:
res/layout/filesystem_info_dialog.xml
res/layout/fso_properties_dialog.xml
res/values/overlay.xml [new file with mode: 0644]
res/values/strings.xml
res/xml/command_list.xml
src/com/cyanogenmod/explorer/activities/NavigationActivity.java
src/com/cyanogenmod/explorer/activities/SearchActivity.java
src/com/cyanogenmod/explorer/commands/AsyncResultListener.java
src/com/cyanogenmod/explorer/commands/ExecutableCreator.java
src/com/cyanogenmod/explorer/commands/FindExecutable.java
src/com/cyanogenmod/explorer/commands/FolderUsageExecutable.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/shell/FindCommand.java
src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/commands/shell/ProcessIdCommand.java
src/com/cyanogenmod/explorer/commands/shell/Shell.java
src/com/cyanogenmod/explorer/commands/shell/ShellExecutableCreator.java
src/com/cyanogenmod/explorer/console/shell/ShellConsole.java
src/com/cyanogenmod/explorer/model/FolderUsage.java [new file with mode: 0644]
src/com/cyanogenmod/explorer/ui/dialogs/FsoPropertiesDialog.java
src/com/cyanogenmod/explorer/util/CommandHelper.java
src/com/cyanogenmod/explorer/util/FileHelper.java
src/com/cyanogenmod/explorer/util/MimeTypeHelper.java
src/com/cyanogenmod/explorer/util/ParseHelper.java
tests/AndroidManifest.xml
tests/src/com/cyanogenmod/explorer/commands/shell/FindCommandTest.java
tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java [new file with mode: 0644]
tests/src/com/cyanogenmod/explorer/commands/shell/ProcessIdCommandTest.java

index 3103e60..9a15b1c 100644 (file)
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
index 08e1586..45fe012 100644 (file)
@@ -15,7 +15,7 @@
  ** limitations under the License.
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="wrap_content"
+  android:layout_width="match_parent"
   android:layout_height="wrap_content" >
 
   <!-- Tabs -->
@@ -93,6 +93,7 @@
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
+        android:textAppearance="@style/secondary_text_appearance" />
+    </TableRow>
+
+    <!-- Contains -->
+    <TableRow
+      android:id="@+id/fso_properties_contains_row"
+      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:visibility="gone" >
+
+      <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/default_margin"
+        android:gravity="left|center_vertical"
+        android:text="@string/fso_properties_dialog_contains"
+        android:textAppearance="@style/primary_text_appearance" />
+
+      <TextView
+        android:id="@+id/fso_properties_contains"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/default_margin"
+        android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
         android:layout_height="wrap_content"
         android:layout_margin="@dimen/default_margin"
         android:gravity="left|center_vertical"
+        android:singleLine="false"
         android:textAppearance="@style/secondary_text_appearance" />
     </TableRow>
 
diff --git a/res/values/overlay.xml b/res/values/overlay.xml
new file mode 100644 (file)
index 0000000..36e1238
--- /dev/null
@@ -0,0 +1,33 @@
+<?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.
+-->
+
+<!--
+  This file contains values that could be overlayed. This allow
+  configure special values for each device. Use overlay building folder on device tree
+  for overlay this values
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+  <!-- The buffer use to read files (in bytes). Default: 0.5 Mb -->
+  <integer name="read_buffer_size">512</integer>
+
+  <!-- The maximum file size that the app is able to read. Beyond this size the user will get a
+       warning message (in bytes). Default: 5 Mb
+       -->
+  <integer name="max_read_file_size">5120</integer>
+
+</resources>
\ No newline at end of file
index 98ba532..47948e4 100644 (file)
 
   <!-- Loading waiting message -->
   <string name="loading_message">Loading&#8230;</string>
+  <!-- Computing message -->
+  <string name="computing_message">Computing&#8230; <xliff:g id="data">%1$s</xliff:g></string>
+  <!-- Computing new line message -->
+  <string name="computing_message_ln">Computing&#8230;\n<xliff:g id="data">%1$s</xliff:g></string>
+  <!-- Cancelled message -->
+  <string name="cancelled_message">Cancelled.</string>
   <!-- Error message -->
   <string name="error_message">Error.</string>
 
   <string name="fso_properties_dialog_link">Link:</string>
   <!-- Fso Properties Dialog * Size Label -->
   <string name="fso_properties_dialog_size">Size:</string>
+  <!-- Fso Properties Dialog * Contains Label -->
+  <string name="fso_properties_dialog_contains">Contains:</string>
   <!-- Fso Properties Dialog * Date Label -->
   <string name="fso_properties_dialog_date">Last Access:</string>
   <!-- Fso Properties Dialog * Owner Label -->
   <string name="fso_properties_dialog_write" translatable="false">W</string>
   <!-- Fso Properties Dialog * Execute Label -->
   <string name="fso_properties_dialog_execute" translatable="false">X</string>
+  <!-- Fso Properties Dialog * Execute Label -->
+  <string name="fso_properties_dialog_folder_items">
+    <xliff:g id="folders">%1$s</xliff:g> / <xliff:g id="files">%2$s</xliff:g></string>
+  <!-- Fso Properties Dialog * Folders -->
+  <plurals name="fso_properties_dialog_folders">
+    <item quantity="zero">0 folders</item>
+    <item quantity="one">1 folder</item>
+    <item quantity="other"><xliff:g id="folders">%1$d</xliff:g> folders</item>
+  </plurals>
+  <!-- Fso Properties Dialog * Files -->
+  <plurals name="fso_properties_dialog_files">
+    <item quantity="zero">0 files</item>
+    <item quantity="one">1 files</item>
+    <item quantity="other"><xliff:g id="files">%1$d</xliff:g> files</item>
+  </plurals>
 
   <!-- Choose console dialog title -->
   <string name="choose_console_dialog_title">Choose console</string>
index d169369..489ea25 100644 (file)
@@ -44,6 +44,7 @@
   <command commandId="echo" commandPath="/system/xbin/echo" commandArgs="%1$s" />
   <command commandId="fileinfo" commandPath="/system/bin/ls" commandArgs="-ald %1$s" />
   <command commandId="find" commandPath="/system/xbin/find" commandArgs="%1$s \\( -name %2$s -o -name %3$s -o -name %4$s -o -name %5$s -o -name %6$s \\) -exec /system/xbin/echo {} \\; -exec /system/bin/ls -ald {} \\;" />
+  <command commandId="folderusage" commandPath="/system/bin/ls" commandArgs="-alR %1$s" />
   <command commandId="groups" commandPath="/system/xbin/groups" commandArgs="" />
   <command commandId="id" commandPath="/system/bin/id" commandArgs="-Gn" />
   <command commandId="ls" commandPath="cd" commandArgs="%1$s &amp;&amp; /system/bin/ls -al %1$s | /system/xbin/grep -v -e '->' &amp;&amp; echo '>SIMLINKS>' &amp;&amp; /system/bin/ls -al %1$s | { /system/xbin/grep -e '->' || true; } &amp;&amp; echo '>SIMLINKS_DATA>' &amp;&amp; /system/bin/ls -aF %1$s | /system/xbin/grep -e '^l' | /system/xbin/awk '{print $2}' | /system/xbin/xargs -r -n1 /system/xbin/readlink -f &amp;&amp; /system/bin/ls -F %1$s | /system/xbin/grep -e '^l' | /system/xbin/awk '{print $2}' | /system/xbin/xargs -r -n1 /system/xbin/readlink -f | { /system/xbin/xargs -r -n1 /system/bin/ls -ald || true; }" />
   <command commandId="mount" commandPath="/system/bin/mount" commandArgs="-o %1$s,remount -t auto %2$s %3$s" />
   <command commandId="mountpointinfo" commandPath="/system/bin/cat" commandArgs="/proc/mounts" />
   <command commandId="mv" commandPath="/system/bin/mv" commandArgs="%1$s %2$s" />
-  <command commandId="pid" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep `ps | /system/xbin/grep %1$s | /system/xbin/awk '{print $1}'` | grep %2$s | /system/xbin/awk '{print $2}'" />
+  <command commandId="pid_shell" commandPath="/system/xbin/echo" commandArgs="$$" />
+  <command commandId="pid_cmd" commandPath="/system/bin/ps" commandArgs="| /system/xbin/grep %1$s | /system/xbin/grep -w %2$s | /system/xbin/awk '{print $2}'" />
   <command commandId="pwd" commandPath="/system/xbin/pwd" commandArgs="-P" />
   <command commandId="quickfoldersearch" commandPath="/system/bin/ls" commandArgs="-aFd %1$s.* %1$s* | /system/xbin/grep -e '^d' -e '^ld' | /system/xbin/awk '{print $2}'" />
-  <command commandId="readlink" commandPath="cd" commandArgs="`/system/xbin/dirname %1$s` &amp;&amp; /system/xbin/readlink -f %1$s | awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/xbin/dirname &amp;&amp; /system/xbin/readlink -f %1$s | awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/bin/ls -ald" />
+  <command commandId="readlink" commandPath="cd" commandArgs="`/system/xbin/dirname %1$s` &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/xbin/dirname &amp;&amp; /system/xbin/readlink -f %1$s | /system/xbin/awk -F// '{print &quot;\\&quot;&quot;$1&quot;\\&quot;&quot;}' | /system/xbin/xargs -n1 /system/bin/ls -ald" />
   <command commandId="rm" commandPath="/system/bin/rm" commandArgs="%1$s" />
   <command commandId="rmdir" commandPath="/system/bin/rm" commandArgs="-r %1$s" />
   <command commandId="touch" commandPath="/system/xbin/echo" commandArgs="-n '' >> %1$s" />
index 5631e5b..13dca60 100644 (file)
@@ -985,9 +985,9 @@ public class NavigationActivity extends Activity
 
         //Do back operation over the navigation history
         boolean flag = this.mExitFlag;
-        
+
         this.mExitFlag = !back();
-        
+
         // Retrieve if the exit status timeout has expired
         long now = System.currentTimeMillis();
         boolean timeout = (this.mExitBackTimeout == -1 ||
index 8fe4dc2..2f94b72 100644 (file)
@@ -541,7 +541,8 @@ public class SearchActivity extends Activity
                                         if (SearchActivity.this.mExecutable.cancel()) {
                                             if (SearchActivity.this.mAdapter != null) {
                                                 SearchActivity.this.toggleResults(
-                                                   SearchActivity.this.mAdapter.getCount() > 0);
+                                                   SearchActivity.this.
+                                                       mAdapter.getCount() > 0, true);
                                             }
                                             return true;
                                         }
@@ -598,7 +599,7 @@ public class SearchActivity extends Activity
                 //Toggle results
                 List<SearchResult> list = SearchActivity.this.mRestoreState.getSearchResultList();
                 String directory = SearchActivity.this.mRestoreState.getSearchDirectory();
-                SearchActivity.this.toggleResults(list.size() > 0);
+                SearchActivity.this.toggleResults(list.size() > 0, true);
                 setFoundItems(list.size(), directory);
 
                 //Set terms
@@ -672,17 +673,18 @@ public class SearchActivity extends Activity
         this.mAdapter.clear();
         this.mAdapter.notifyDataSetChanged();
         this.mSearchListView.setSelection(0);
-        toggleResults(false);
+        toggleResults(false, true);
     }
 
     /**
      * Method that toggle the views when there are results.
      *
      * @param hasResults Indicates if there are results
+     * @param showEmpty Show the empty list message
      */
-    private void toggleResults(boolean hasResults) {
+    private void toggleResults(boolean hasResults, boolean showEmpty) {
         this.mSearchListView.setVisibility(hasResults ? View.VISIBLE : View.INVISIBLE);
-        this.mEmptyListMsg.setVisibility(!hasResults ? View.VISIBLE : View.INVISIBLE);
+        this.mEmptyListMsg.setVisibility(!hasResults && showEmpty ? View.VISIBLE : View.INVISIBLE);
     }
 
     /**
@@ -866,7 +868,7 @@ public class SearchActivity extends Activity
             @Override
             @SuppressWarnings("synthetic-access")
             public void run() {
-                SearchActivity.this.toggleResults(false);
+                SearchActivity.this.toggleResults(false, false);
             }
         });
     }
@@ -901,31 +903,13 @@ public class SearchActivity extends Activity
     }
 
     /**
-     * Method that draw the results in the listview
-     */
-    private void drawResults() {
-        //Toggle results
-        this.toggleResults(this.mResultList.size() > 0);
-        setFoundItems(this.mResultList.size(), this.mSearchDirectory);
-
-        //Create the task for drawing the data
-        this.mDrawingSearchResultTask =
-                                new SearchResultDrawingAsyncTask(
-                                        this.mSearchListView,
-                                        this.mSearchWaiting,
-                                        this,
-                                        this.mResultList,
-                                        this.mQuery);
-        this.mDrawingSearchResultTask.execute();
-    }
-
-    /**
      * {@inheritDoc}
      */
     @Override
-    public void onPartialResult(final List<FileSystemObject> partialResults) {
+    @SuppressWarnings("unchecked")
+    public void onPartialResult(final Object partialResults) {
       //Saved in the global result list, for save at the end
-        SearchActivity.this.mResultList.addAll(partialResults);
+        SearchActivity.this.mResultList.addAll((List<FileSystemObject>)partialResults);
 
         //Notify progress
         this.mSearchListView.post(new Runnable() {
@@ -950,6 +934,25 @@ public class SearchActivity extends Activity
     }
 
     /**
+     * Method that draw the results in the listview
+     */
+    private void drawResults() {
+        //Toggle results
+        this.toggleResults(this.mResultList.size() > 0, true);
+        setFoundItems(this.mResultList.size(), this.mSearchDirectory);
+
+        //Create the task for drawing the data
+        this.mDrawingSearchResultTask =
+                                new SearchResultDrawingAsyncTask(
+                                        this.mSearchListView,
+                                        this.mSearchWaiting,
+                                        this,
+                                        this.mResultList,
+                                        this.mQuery);
+        this.mDrawingSearchResultTask.execute();
+    }
+
+    /**
      * Method that creates a {@link SearchInfoParcelable} reference from
      * the current data.
      *
index e0f2a14..defa930 100644 (file)
@@ -16,9 +16,6 @@
 
 package com.cyanogenmod.explorer.commands;
 
-import com.cyanogenmod.explorer.model.FileSystemObject;
-
-import java.util.List;
 
 /**
  * An interface for communicate partial results.
@@ -39,9 +36,9 @@ public interface AsyncResultListener {
     /**
      * Method invoked when new partial data are ready.
      *
-     * @param results New data results
+     * @param result New data result
      */
-    void onPartialResult(List<FileSystemObject> results);
+    void onPartialResult(Object result);
 
     /**
      * Method invoked when an exception occurs while executing the program.
index 3381003..e024b9e 100644 (file)
@@ -80,10 +80,12 @@ public interface ExecutableCreator {
      * Method that creates an executable for create a new directory.
      *
      * @param dir The absolute path of the new directory
-     * @return CreateDirExecutable A {@link CreateDirExecutable} executable implementation reference
+     * @return CreateDirExecutable A {@link CreateDirExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
-    CreateDirExecutable createCreateDirectoryExecutable(String dir) throws CommandNotFoundException;
+    CreateDirExecutable createCreateDirectoryExecutable(String dir)
+            throws CommandNotFoundException;
 
     /**
      * Method that creates an executable for create a new file.
@@ -108,7 +110,8 @@ public interface ExecutableCreator {
      * Method that creates an executable for delete a directory.
      *
      * @param dir The absolute path to the directory to be deleted
-     * @return DeleteDirExecutable A {@link DeleteDirExecutable} executable implementation reference
+     * @return DeleteDirExecutable A {@link DeleteDirExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     DeleteDirExecutable createDeleteDirExecutable(String dir) throws CommandNotFoundException;
@@ -127,7 +130,8 @@ public interface ExecutableCreator {
      * Method that creates an executable for retrieve the disk usage.
      * for all filesystems
      *
-     * @return DiskUsageExecutable A {@link DiskUsageExecutable} executable implementation reference
+     * @return DiskUsageExecutable A {@link DiskUsageExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     DiskUsageExecutable createDiskUsageExecutable() throws CommandNotFoundException;
@@ -137,7 +141,8 @@ public interface ExecutableCreator {
      * of the filesystem of a directory
      *
      * @param dir The absolute path to the directory
-     * @return DiskUsageExecutable A {@link DiskUsageExecutable} executable implementation reference
+     * @return DiskUsageExecutable A {@link DiskUsageExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     DiskUsageExecutable createDiskUsageExecutable(String dir) throws CommandNotFoundException;
@@ -153,7 +158,7 @@ public interface ExecutableCreator {
     EchoExecutable createEchoExecutable(String msg) throws CommandNotFoundException;
 
     /**
-     * Method that creates an executable for make search over the filesystem.
+     * Method that creates an executable for make searches over the filesystem.
      *
      * @param directory The directory where to search
      * @param query The term of the query
@@ -166,6 +171,19 @@ public interface ExecutableCreator {
             throws CommandNotFoundException;
 
     /**
+     * Method that creates an executable for compute the disk usage of a folder.
+     *
+     * @param directory The directory where to search
+     * @param asyncResultListener The listener where to return partial results
+     * @return FolderUsageExecutable A {@link FolderUsageExecutable} executable
+     * implementation reference
+     * @throws CommandNotFoundException If the executable can't be created
+     */
+    FolderUsageExecutable createFolderUsageExecutable(
+            String directory, AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException;
+
+    /**
      * Method that creates an executable for retrieve the groups of the current user.
      *
      * @return GroupsExecutable A {@link GroupsExecutable} executable implementation reference
@@ -191,7 +209,8 @@ public interface ExecutableCreator {
      * @throws CommandNotFoundException If the executable can't be created
      * @see LIST_MODE
      */
-    ListExecutable createListExecutable(String src, LIST_MODE mode) throws CommandNotFoundException;
+    ListExecutable createListExecutable(String src, LIST_MODE mode)
+            throws CommandNotFoundException;
 
     /**
      * Method that creates an executable for retrieve identity information of the current user.
@@ -229,28 +248,42 @@ public interface ExecutableCreator {
      * of a file system object.
      *
      * @param fso The absolute path to the file system object
-     * @return ParentDirExecutable A {@link ParentDirExecutable} executable implementation reference
+     * @return ParentDirExecutable A {@link ParentDirExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     ParentDirExecutable createParentDirExecutable(String fso) throws CommandNotFoundException;
 
     /**
      * Method that creates an executable for retrieve operating system process identifier of a
+     * shell.
+     *
+     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation
+     * reference
+     * @throws CommandNotFoundException If the executable can't be created
+     */
+    ProcessIdExecutable createShellProcessIdExecutable() throws CommandNotFoundException;
+
+    /**
+     * Method that creates an executable for retrieve operating system process identifier of a
      * process.
      *
+     * @param pid The shell process id where the process is running
      * @param processName The process name
-     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation reference
+     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     ProcessIdExecutable createProcessIdExecutable(
-            String processName) throws CommandNotFoundException;
+            int pid, String processName) throws CommandNotFoundException;
 
     /**
      * Method that creates an executable for quickly retrieve the name of directories
      * that matches a string.
      *
      * @param regexp The regular expression
-     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation reference
+     * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation
+     * reference
      * @throws CommandNotFoundException If the executable can't be created
      */
     QuickFolderSearchExecutable createQuickFolderSearchExecutable(
index b4a8dc9..ad08716 100644 (file)
@@ -17,7 +17,7 @@
 package com.cyanogenmod.explorer.commands;
 
 /**
- * An interface that represents an executable for make search over
+ * An interface that represents an executable for make search over
  * the filesystem.
  */
 public interface FindExecutable extends AsyncResultExecutable {
diff --git a/src/com/cyanogenmod/explorer/commands/FolderUsageExecutable.java b/src/com/cyanogenmod/explorer/commands/FolderUsageExecutable.java
new file mode 100644 (file)
index 0000000..cc1b6a4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.explorer.commands;
+
+/**
+ * An interface that represents an executable for retrieve a folder usage
+ */
+public interface FolderUsageExecutable extends AsyncResultExecutable {
+    /**NON BLOCK**/
+}
index e519171..5168c16 100644 (file)
@@ -38,7 +38,7 @@ import java.util.List;
 /**
  * A class for search files.
  *
- * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?ls"}
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?find"}
  */
 public class FindCommand extends AsyncResultProgram implements FindExecutable {
 
@@ -60,8 +60,7 @@ public class FindCommand extends AsyncResultProgram implements FindExecutable {
     private String mPartial;
 
     /**
-     * Constructor of <code>FindCommand</code>. This constructor uses a
-     * <code>DIRECTORY</code> mode as listing mode.
+     * Constructor of <code>FindCommand</code>.
      *
      * @param directory The "absolute" directory where start the search
      * @param query The terms to be searched
@@ -202,8 +201,10 @@ public class FindCommand extends AsyncResultProgram implements FindExecutable {
         //Search in a subdirectory without permissions returns 1, but this
         //not must be treated as an error
         //Ignore exit code 143 (canceled)
-        if (exitCode != 0 && exitCode != 1 && exitCode != 143) {
-            throw new ExecutionException("exitcode != 0 && != 1 && != 143"); //$NON-NLS-1$
+        //Ignore exit code 137 (kill -9)
+        if (exitCode != 0 && exitCode != 1 && exitCode != 143 && exitCode != 137) {
+            throw new ExecutionException(
+                        "exitcode != 0 && != 1 && != 143 && != 137"); //$NON-NLS-1$
         }
     }
 
diff --git a/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java b/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommand.java
new file mode 100644 (file)
index 0000000..b662c57
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * 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.explorer.commands.shell;
+
+import android.util.Log;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
+import com.cyanogenmod.explorer.console.CommandNotFoundException;
+import com.cyanogenmod.explorer.console.ExecutionException;
+import com.cyanogenmod.explorer.console.InsufficientPermissionsException;
+import com.cyanogenmod.explorer.model.Directory;
+import com.cyanogenmod.explorer.model.FileSystemObject;
+import com.cyanogenmod.explorer.model.FolderUsage;
+import com.cyanogenmod.explorer.model.Symlink;
+import com.cyanogenmod.explorer.util.FileHelper;
+import com.cyanogenmod.explorer.util.MimeTypeHelper;
+import com.cyanogenmod.explorer.util.MimeTypeHelper.MimeTypeCategory;
+import com.cyanogenmod.explorer.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for retrieve the disk usage of a folder
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?ls"}
+ */
+public class FolderUsageCommand extends AsyncResultProgram implements FolderUsageExecutable {
+
+    private static final String TAG = "FolderUsageCommand"; //$NON-NLS-1$
+
+    private static final String ID_FOLDER_USAGE_DIRECTORY = "folderusage"; //$NON-NLS-1$
+
+    private final String mDirectory;
+    private FolderUsage mFolderUsage;
+    private String mPartial;
+
+    /**
+     * Constructor of <code>FolderUsageCommand</code>.
+     *
+     * @param directory The "absolute" directory to compute
+     * @param asyncResultListener The partial result listener
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public FolderUsageCommand(
+            String directory, AsyncResultListener asyncResultListener)
+            throws InvalidCommandDefinitionException {
+        super(ID_FOLDER_USAGE_DIRECTORY, asyncResultListener, new String[]{directory});
+        this.mFolderUsage = new FolderUsage(directory);
+        this.mPartial = ""; //$NON-NLS-1$
+        this.mDirectory = directory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onStartParsePartialResult() {
+        this.mFolderUsage = new FolderUsage(this.mDirectory);
+        this.mPartial = ""; //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onEndParsePartialResult(boolean cancelled) {
+        this.mPartial = ""; //$NON-NLS-1$
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onParsePartialResult(final String partialIn) {
+
+        // Check the in buffer to extract information
+        BufferedReader br = null;
+        try {
+            //Read the partial + previous partial and clean partial
+            br = new BufferedReader(new StringReader(this.mPartial + partialIn));
+            this.mPartial = ""; //$NON-NLS-1$
+
+            //Add all lines to an array
+            List<String> lines = new ArrayList<String>();
+            String line = null;
+            while ((line = br.readLine()) != null) {
+                if (line.trim().length() == 0) {
+                    continue;
+                }
+                lines.add(line);
+            }
+
+            //2 lines per file system object translation
+            boolean newData = false;
+            int c = 0;
+            while (lines.size() > 0) {
+                try {
+                    // Retrieve the info
+                    String szLine = lines.get(0).trim();
+
+                    // Parent folder is not necessary here. Only the information relative to
+                    // type and size
+                    FileSystemObject fso =
+                            ParseHelper.toFileSystemObject(
+                                    FileHelper.ROOT_DIRECTORY, szLine, true);
+
+                    // Only regular files or directories. No compute Symlinks
+                    if (fso instanceof Symlink) {
+
+                    // Directory
+                    } else if (fso instanceof Directory) {
+                        // Folder
+                        this.mFolderUsage.addFolder();
+                        newData = true;
+
+                    // Regular File, Block device, ...
+                    } else {
+                        this.mFolderUsage.addFile();
+                        // Compute statistics and size
+                        MimeTypeCategory category =
+                                MimeTypeHelper.getCategory(null, fso);
+                        this.mFolderUsage.addFileToCategory(category);
+                        this.mFolderUsage.addSize(fso.getSize());
+                        newData = true;
+                    }
+
+                    // Partial notification
+                    if (c % 5 == 0) {
+                        //If a listener is defined, then send the partial result
+                        if (getAsyncResultListener() != null && newData) {
+                            getAsyncResultListener().onPartialResult(this.mFolderUsage);
+                        }
+                    }
+
+                } catch (Exception ex) { /**NON BLOCK **/ }
+
+                //Remove the the line
+                lines.remove(0);
+            }
+
+            //Saves the lines for the next partial read (At this point only one line
+            //can exists in the buffer. The rest was processed or discarded)
+            if (lines.size() > 0) {
+                this.mPartial = lines.get(0).concat(FileHelper.NEWLINE);
+            }
+
+            //If a listener is defined, then send the partial result
+            if (getAsyncResultListener() != null && newData) {
+                getAsyncResultListener().onPartialResult(this.mFolderUsage);
+            }
+
+        } catch (Exception ex) {
+            Log.w(TAG, "Partial result fails", ex); //$NON-NLS-1$
+
+        } finally {
+            try {
+                if (br != null) {
+                    br.close();
+                }
+            } catch (Throwable ex) {
+                /**NON BLOCK**/
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isIgnoreShellStdErrCheck() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void checkExitCode(int exitCode)
+            throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+
+        //Access a subdirectory without permissions returns 1, but this
+        //not must be treated as an error
+        //Ignore exit code 143 (canceled)
+        //Ignore exit code 137 (kill -9)
+        if (exitCode != 0 && exitCode != 1 && exitCode != 143 && exitCode != 137) {
+            throw new ExecutionException(
+                        "exitcode != 0 && != 1 && != 143 && != 137"); //$NON-NLS-1$
+        }
+    }
+}
index 228bd96..fc20203 100644 (file)
@@ -34,17 +34,30 @@ import java.text.ParseException;
  */
 public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExecutable {
 
-    private static final String ID = "pid";  //$NON-NLS-1$
+    private static final String ID_SHELL = "pid_shell";  //$NON-NLS-1$
+    private static final String ID_CMD = "pid_cmd";  //$NON-NLS-1$
     private Integer mPID;
 
     /**
-     * Constructor of <code>ProcessIdCommand</code>.
+     * Constructor of <code>ProcessIdCommand</code>.<br/>
+     * Use this to retrieve the PID of a shell.
      *
+     * @throws InvalidCommandDefinitionException If the command has an invalid definition
+     */
+    public ProcessIdCommand() throws InvalidCommandDefinitionException {
+        super(ID_SHELL);
+    }
+
+    /**
+     * Constructor of <code>ProcessIdCommand</code>.<br/>
+     * Use this to retrieve the PID of a command running on a shell.
+     *
+     * @param pid The process identifier of the shell when the process is running
      * @param processName The process name
      * @throws InvalidCommandDefinitionException If the command has an invalid definition
      */
-    public ProcessIdCommand(String processName) throws InvalidCommandDefinitionException {
-        super(ID, processName);
+    public ProcessIdCommand(int pid, String processName) throws InvalidCommandDefinitionException {
+        super(ID_CMD, new String[]{processName, String.valueOf(pid)});
     }
 
     /**
index 9b05326..bb31123 100644 (file)
@@ -31,6 +31,8 @@ import com.cyanogenmod.explorer.console.ReadOnlyFilesystemException;
  */
 public abstract class Shell extends Command {
 
+    private int mPid;
+
     /**
      * @Constructor of <code>Shell</code>
      *
@@ -41,6 +43,25 @@ public abstract class Shell extends Command {
      */
     public Shell(String id, String... args) throws InvalidCommandDefinitionException {
         super(id, args);
+        this.mPid = -1;
+    }
+
+    /**
+     * Method that returns the process identifier of the console
+     *
+     * @return int The process identifier
+     */
+    public final int getPid() {
+        return this.mPid;
+    }
+
+    /**
+     * Method that sets the process identifier of the console
+     *
+     * @param pid The process identifier
+     */
+    public final void setPid(int pid) {
+        this.mPid = pid;
     }
 
     /**
index a4e3dca..b13afb1 100644 (file)
@@ -30,6 +30,7 @@ import com.cyanogenmod.explorer.commands.DiskUsageExecutable;
 import com.cyanogenmod.explorer.commands.EchoExecutable;
 import com.cyanogenmod.explorer.commands.ExecutableCreator;
 import com.cyanogenmod.explorer.commands.FindExecutable;
+import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
 import com.cyanogenmod.explorer.commands.GroupsExecutable;
 import com.cyanogenmod.explorer.commands.IdentityExecutable;
 import com.cyanogenmod.explorer.commands.ListExecutable;
@@ -237,6 +238,20 @@ public class ShellExecutableCreator implements ExecutableCreator {
      * {@inheritDoc}
      */
     @Override
+    public FolderUsageExecutable createFolderUsageExecutable(
+            String directory, AsyncResultListener asyncResultListener)
+            throws CommandNotFoundException {
+        try {
+            return new FolderUsageCommand(directory, asyncResultListener);
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("FolderUsageCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public GroupsExecutable createGroupsExecutable() throws CommandNotFoundException {
         try {
             return new GroupsCommand();
@@ -326,10 +341,22 @@ public class ShellExecutableCreator implements ExecutableCreator {
      * {@inheritDoc}
      */
     @Override
-    public ProcessIdExecutable createProcessIdExecutable(String processName)
+    public ProcessIdExecutable createShellProcessIdExecutable() throws CommandNotFoundException {
+        try {
+            return new ProcessIdCommand();
+        } catch (InvalidCommandDefinitionException icdEx) {
+            throw new CommandNotFoundException("ProcessIdCommand", icdEx); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ProcessIdExecutable createProcessIdExecutable(int pid, String processName)
             throws CommandNotFoundException {
         try {
-            return new ProcessIdCommand(processName);
+            return new ProcessIdCommand(pid, processName);
         } catch (InvalidCommandDefinitionException icdEx) {
             throw new CommandNotFoundException("ProcessIdCommand", icdEx); //$NON-NLS-1$
         }
index c6ae3d5..d5f6884 100644 (file)
@@ -24,6 +24,7 @@ import com.cyanogenmod.explorer.commands.Executable;
 import com.cyanogenmod.explorer.commands.ExecutableFactory;
 import com.cyanogenmod.explorer.commands.GroupsExecutable;
 import com.cyanogenmod.explorer.commands.IdentityExecutable;
+import com.cyanogenmod.explorer.commands.ProcessIdExecutable;
 import com.cyanogenmod.explorer.commands.shell.AsyncResultProgram;
 import com.cyanogenmod.explorer.commands.shell.Command;
 import com.cyanogenmod.explorer.commands.shell.InvalidCommandDefinitionException;
@@ -220,6 +221,18 @@ public abstract class ShellConsole extends Console {
                 }
             }
 
+            // Retrieve the PID of the shell
+            ProcessIdExecutable processIdCmd =
+                    getExecutableFactory().
+                        newCreator().createShellProcessIdExecutable();
+            execute(processIdCmd);
+            Integer pid = processIdCmd.getResult();
+            if (pid == null) {
+                throw new ConsoleAllocException(
+                        "Can't retrieve the PID of the shell."); //$NON-NLS-1$
+            }
+            this.mShell.setPid(pid.intValue());
+
             //Retrieve identity
             IdentityExecutable identityCmd =
                     getExecutableFactory().newCreator().createIdentityExecutable();
@@ -452,6 +465,14 @@ public abstract class ShellConsole extends Console {
 
             //Retrieve exit code
             int exitCode = getExitCode(this.mSbIn);
+            if (isTrace()) {
+                Log.v(TAG,
+                        String.format("%s-%s, command: %s, exitCode: %s",  //$NON-NLS-1$
+                                ShellConsole.this.mShell.getId(),
+                                program.getId(),
+                                cmd,
+                                String.valueOf(exitCode)));
+            }
 
             //Check if invocation was successfully or not
             if (!program.isIgnoreShellStdErrCheck()) {
@@ -805,6 +826,7 @@ public abstract class ShellConsole extends Console {
                         Integer pid =
                                 CommandHelper.getProcessId(
                                         null,
+                                        this.mShell.getPid(),
                                         program.getCommand(),
                                         ExplorerApplication.getBackgroundConsole());
                         if (pid != null) {
diff --git a/src/com/cyanogenmod/explorer/model/FolderUsage.java b/src/com/cyanogenmod/explorer/model/FolderUsage.java
new file mode 100644 (file)
index 0000000..0d6339e
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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.explorer.model;
+
+import android.util.SparseArray;
+
+import com.cyanogenmod.explorer.util.MimeTypeHelper.MimeTypeCategory;
+
+import java.io.Serializable;
+
+/**
+ * A class that holds information about the usage of a folder (space and number of files/folders).
+ */
+public class FolderUsage implements Serializable, Cloneable {
+
+    private static final long serialVersionUID = -8830510087518648692L;
+
+    private final String mFolder;
+    private int mNumberOfFolders;
+    private int mNumberOfFiles;
+    private long mTotalSize;
+    private SparseArray<Long> mStatistics;
+
+    /**
+     * Constructor of <code>FolderUsage</code>.
+     *
+     * @param folder The folder of which retrieve the usage
+     */
+    public FolderUsage(String folder) {
+        super();
+
+        // Initialize the class
+        this.mFolder = folder;
+        this.mNumberOfFolders = 0;
+        this.mNumberOfFiles = 0;
+        this.mTotalSize = 0;
+
+        // Fill the array of statistics
+        MimeTypeCategory[] categories = MimeTypeCategory.values();
+        this.mStatistics = new SparseArray<Long>(categories.length-1);
+        for (int i=0; i<categories.length; i++) {
+            this.mStatistics.put(categories[i].ordinal(), Long.valueOf(0));
+        }
+    }
+
+    /**
+     * Method that adds 1 folder to the total number of folders.
+     */
+    public void addFolder() {
+        this.mNumberOfFolders ++;
+    }
+
+    /**
+     * Method that adds 1 file to the total number of files.
+     */
+    public void addFile() {
+        this.mNumberOfFiles ++;
+    }
+
+    /**
+     * Method that adds to the total size.
+     *
+     * @param size The size to add to the total
+     */
+    public void addSize(long size) {
+        this.mTotalSize += size;
+    }
+
+    /**
+     * Method that add a file to the category
+     *
+     * @param category The category
+     */
+    public void addFileToCategory(MimeTypeCategory category) {
+        long count = this.mStatistics.get(category.ordinal()).longValue();
+        count++;
+        this.mStatistics.put(category.ordinal(), Long.valueOf(count));
+    }
+
+    /**
+     * Method that returns the folder of which retrieve the usage.
+     *
+     * @return String The folder of which retrieve the usage
+     */
+    public String getFolder() {
+        return this.mFolder;
+    }
+
+    /**
+     * Method that returns the total number of folders.
+     *
+     * @return int The total number of folders
+     */
+    public int getNumberOfFolders() {
+        return this.mNumberOfFolders;
+    }
+
+    /**
+     * Method that returns the total number of files.
+     *
+     * @return int The total number of files
+     */
+    public int getNumberOfFiles() {
+        return this.mNumberOfFiles;
+    }
+
+    /**
+     * Method that returns the total size.
+     *
+     * @return long The total size
+     */
+    public long getTotalSize() {
+        return this.mTotalSize;
+    }
+
+    /**
+     * Method sets the total size.
+     *
+     * @param totalSize The total size
+     */
+    public void setTotalSize(long totalSize) {
+        this.mTotalSize = totalSize;
+    }
+
+    /**
+     * Method that returns the number of files for a {@link MimeTypeCategory}.
+     *
+     * @param category The category
+     * @return long The number of files for the category
+     */
+    public long getStatisticsForCategory(MimeTypeCategory category) {
+        return this.mStatistics.get(category.ordinal()).longValue();
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((this.mFolder == null) ? 0 : this.mFolder.hashCode());
+        result = prime * result + this.mNumberOfFiles;
+        result = prime * result + this.mNumberOfFolders;
+        result = prime * result
+                + ((this.mStatistics == null) ? 0 : this.mStatistics.hashCode());
+        result = prime * result + (int) (this.mTotalSize ^ (this.mTotalSize >>> 32));
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        FolderUsage other = (FolderUsage) obj;
+        if (this.mFolder == null) {
+            if (other.mFolder != null)
+                return false;
+        } else if (!this.mFolder.equals(other.mFolder))
+            return false;
+        if (this.mNumberOfFiles != other.mNumberOfFiles)
+            return false;
+        if (this.mNumberOfFolders != other.mNumberOfFolders)
+            return false;
+        if (this.mStatistics == null) {
+            if (other.mStatistics != null)
+                return false;
+        } else if (!this.mStatistics.equals(other.mStatistics))
+            return false;
+        if (this.mTotalSize != other.mTotalSize)
+            return false;
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#clone()
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        FolderUsage other = new FolderUsage(this.mFolder);
+        other.mNumberOfFolders = this.mNumberOfFolders;
+        other.mNumberOfFiles = this.mNumberOfFiles;
+        other.mTotalSize = this.mTotalSize;
+        other.mStatistics = this.mStatistics.clone();
+        return super.clone();
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "FolderUsage [folder=" + this.mFolder + //$NON-NLS-1$
+                ", numberOfFolders=" + this.mNumberOfFolders + //$NON-NLS-1$
+                ", numberOfFiles=" + this.mNumberOfFiles + //$NON-NLS-1$
+                ", totalSize=" + this.mTotalSize + //$NON-NLS-1$
+                ", statistics=" + this.mStatistics + "]"; //$NON-NLS-1$//$NON-NLS-2$
+    }
+}
index 27db6bc..8824908 100644 (file)
 
 package com.cyanogenmod.explorer.ui.dialogs;
 
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.os.AsyncTask;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -35,9 +37,12 @@ import android.widget.Spinner;
 import android.widget.TextView;
 
 import com.cyanogenmod.explorer.R;
+import com.cyanogenmod.explorer.commands.AsyncResultExecutable;
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
 import com.cyanogenmod.explorer.console.ConsoleBuilder;
 import com.cyanogenmod.explorer.model.AID;
 import com.cyanogenmod.explorer.model.FileSystemObject;
+import com.cyanogenmod.explorer.model.FolderUsage;
 import com.cyanogenmod.explorer.model.Group;
 import com.cyanogenmod.explorer.model.GroupPermission;
 import com.cyanogenmod.explorer.model.OthersPermission;
@@ -61,7 +66,9 @@ import java.text.DateFormat;
  */
 public class FsoPropertiesDialog
     implements OnClickListener, OnCheckedChangeListener, OnItemSelectedListener,
-    DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
+    DialogInterface.OnCancelListener, DialogInterface.OnDismissListener, AsyncResultListener {
+
+    private static final String TAG = "FsoPropertiesDialog"; //$NON-NLS-1$
 
     private static final String OWNER_TYPE = "owner"; //$NON-NLS-1$
     private static final String GROUP_TYPE = "group"; //$NON-NLS-1$
@@ -86,10 +93,16 @@ public class FsoPropertiesDialog
     private CheckBox[] mChkGroupPermission;
     private CheckBox[] mChkOthersPermission;
     private TextView mInfoMsgView;
+    private TextView mTvSize;
+    private TextView mTvContains;
 
     private boolean mIgnoreCheckEvents;
     private boolean mHasPrivileged;
 
+    private AsyncResultExecutable mFolderUsageExecutable;
+    private FolderUsage mFolderUsage;
+    private boolean mDrawingFolderUsage;
+
     private DialogInterface.OnDismissListener mOnDismissListener;
 
     /**
@@ -185,7 +198,9 @@ public class FsoPropertiesDialog
         TextView tvType = (TextView)contentView.findViewById(R.id.fso_properties_type);
         View vLinkRow = contentView.findViewById(R.id.fso_properties_link_row);
         TextView tvLink = (TextView)contentView.findViewById(R.id.fso_properties_link);
-        TextView tvSize = (TextView)contentView.findViewById(R.id.fso_properties_size);
+        this.mTvSize = (TextView)contentView.findViewById(R.id.fso_properties_size);
+        View vContatinsRow = contentView.findViewById(R.id.fso_properties_contains_row);
+        this.mTvContains = (TextView)contentView.findViewById(R.id.fso_properties_contains);
         TextView tvDate = (TextView)contentView.findViewById(R.id.fso_properties_date);
         this.mSpnOwner = (Spinner)contentView.findViewById(R.id.fso_properties_owner);
         this.mSpnGroup = (Spinner)contentView.findViewById(R.id.fso_properties_group);
@@ -203,10 +218,10 @@ public class FsoPropertiesDialog
         vLinkRow.setVisibility(this.mFso instanceof Symlink ? View.VISIBLE : View.GONE);
         String size = FileHelper.getHumanReadableSize(this.mFso);
         if (size.length() == 0) {
-            //TODO Compute Size
             size = "-"; //$NON-NLS-1$
         }
-        tvSize.setText(size);
+        this.mTvSize.setText(size);
+        this.mTvContains.setText("-");  //$NON-NLS-1$
         DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
         tvDate.setText(df.format(this.mFso.getLastModifiedTime()));
 
@@ -219,6 +234,38 @@ public class FsoPropertiesDialog
         updatePermissions();
 
         // Load owners and groups AIDs in background
+        loadAIDs();
+
+        // Load owners and groups AIDs in background
+        if (FileHelper.isDirectory(this.mFso)) {
+            vContatinsRow.setVisibility(View.VISIBLE);
+            computeFolderUsage();
+        }
+
+        // Check if permissions operations are allowed
+        try {
+            this.mHasPrivileged = ConsoleBuilder.getConsole(this.mContext).isPrivileged();
+        } catch (Throwable ex) {/**NON BLOCK**/}
+        this.mSpnOwner.setEnabled(this.mHasPrivileged);
+        this.mSpnGroup.setEnabled(this.mHasPrivileged);
+        setCheckBoxesPermissionsEnable(this.mChkUserPermission, this.mHasPrivileged);
+        setCheckBoxesPermissionsEnable(this.mChkGroupPermission, this.mHasPrivileged);
+        setCheckBoxesPermissionsEnable(this.mChkOthersPermission, this.mHasPrivileged);
+        if (!this.mHasPrivileged) {
+            this.mInfoMsgView.setVisibility(View.VISIBLE);
+            this.mInfoMsgView.setOnClickListener(this);
+        }
+
+        //Change the tab
+        onClick(this.mInfoViewTab);
+        this.mIgnoreCheckEvents = false;
+    }
+
+    /**
+     * Method that loads the AIDs in background
+     */
+    private void loadAIDs() {
+        // Load owners and groups AIDs in background
         AsyncTask<Void, Void, SparseArray<AID>> aidsTask =
                         new AsyncTask<Void, Void, SparseArray<AID>>() {
             @Override
@@ -272,24 +319,21 @@ public class FsoPropertiesDialog
             }
         };
         aidsTask.execute();
+    }
 
-        // Check if permissions operations are allowed
+    /**
+     * Method that computes the disk usage of the folder in background
+     */
+    private void computeFolderUsage() {
         try {
-            this.mHasPrivileged = ConsoleBuilder.getConsole(this.mContext).isPrivileged();
-        } catch (Throwable ex) {/**NON BLOCK**/}
-        this.mSpnOwner.setEnabled(this.mHasPrivileged);
-        this.mSpnGroup.setEnabled(this.mHasPrivileged);
-        setCheckBoxesPermissionsEnable(this.mChkUserPermission, this.mHasPrivileged);
-        setCheckBoxesPermissionsEnable(this.mChkGroupPermission, this.mHasPrivileged);
-        setCheckBoxesPermissionsEnable(this.mChkOthersPermission, this.mHasPrivileged);
-        if (!this.mHasPrivileged) {
-            this.mInfoMsgView.setVisibility(View.VISIBLE);
-            this.mInfoMsgView.setOnClickListener(this);
+            this.mFolderUsageExecutable =
+                CommandHelper.getFolderUsage(this.mContext, this.mFso.getFullPath(), this, null);
+        } catch (Exception cause) {
+            //Capture the exception
+            ExceptionUtil.translateException(this.mContext, cause, true, false);
+            this.mTvSize.setText(R.string.error_message);
+            this.mTvContains.setText(R.string.error_message);
         }
-
-        //Change the tab
-        onClick(this.mInfoViewTab);
-        this.mIgnoreCheckEvents = false;
     }
 
     /**
@@ -297,6 +341,7 @@ public class FsoPropertiesDialog
      */
     @Override
     public void onDismiss(DialogInterface dialog) {
+        cancelFolderUsageCommand();
         if (this.mOnDismissListener != null) {
             this.mOnDismissListener.onDismiss(dialog);
         }
@@ -307,6 +352,7 @@ public class FsoPropertiesDialog
      */
     @Override
     public void onCancel(DialogInterface dialog) {
+        cancelFolderUsageCommand();
         if (this.mOnDismissListener != null) {
             this.mOnDismissListener.onDismiss(dialog);
         }
@@ -376,6 +422,9 @@ public class FsoPropertiesDialog
         if (this.mIgnoreCheckEvents) return;
 
         try {
+            // Cancel the folder usage command
+            cancelFolderUsageCommand();
+
             // Retrieve the permissions and send to operating system
             Permissions permissions = getPermissions();
             if (!CommandHelper.changePermissions(
@@ -502,6 +551,9 @@ public class FsoPropertiesDialog
             return;
         }
 
+        // Cancel the folder usage command
+        cancelFolderUsageCommand();
+
         // Change the owner and group of the fso
         try {
             if (!CommandHelper.changeOwner(
@@ -788,4 +840,121 @@ public class FsoPropertiesDialog
                 this.mHasPrivileged && msg == null ? View.GONE : View.VISIBLE);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAsyncStart() {
+        this.mDrawingFolderUsage = false;
+        this.mFolderUsage = new FolderUsage(this.mFso.getFullPath());
+        printFolderUsage(true, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAsyncEnd(final boolean cancelled) {
+        printFolderUsage(false, cancelled);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onPartialResult(final Object partialResults) {
+        try {
+            // Do not saturate ui thread
+            if (this.mDrawingFolderUsage) {
+                return;
+            }
+
+            // Clone the reference
+            FsoPropertiesDialog.this.mFolderUsage =
+                    (FolderUsage)(((FolderUsage)partialResults).clone());
+            printFolderUsage(true, false);
+        }catch (Exception ex) {/** NON BLOCK**/}
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onException(Exception cause) {
+        //Capture the exception
+        ExceptionUtil.translateException(this.mContext, cause);
+    }
+
+    /**
+     * Method that cancels the folder usage command execution
+     */
+    private void cancelFolderUsageCommand() {
+        // Cancel the folder usage command
+        try {
+            if (this.mFolderUsageExecutable != null &&
+                this.mFolderUsageExecutable.isCancelable() &&
+                !this.mFolderUsageExecutable.isCanceled()) {
+                this.mFolderUsageExecutable.cancel();
+            }
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to cancel the folder usage command", ex); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Method that redraws the information about folder usage
+     *
+     * @param computing If the process if computing the data
+     * @param cancelled If the process was cancelled
+     */
+    private void printFolderUsage(final boolean computing, final boolean cancelled) {
+        // Mark that a drawing is in progress
+        this.mDrawingFolderUsage = true;
+
+        final Resources res = this.mContext.getResources();
+        if (cancelled) {
+            FsoPropertiesDialog.this.mTvSize.setText(R.string.cancelled_message);
+            FsoPropertiesDialog.this.mTvContains.setText(R.string.cancelled_message);
+
+            // End of drawing
+            this.mDrawingFolderUsage = false;
+        } else {
+            // Calculate size prior to use ui thread
+            final String size = FileHelper.getHumanReadableSize(this.mFolderUsage.getTotalSize());
+
+            // Compute folders and files string
+            String folders = res.getQuantityString(
+                                        R.plurals.fso_properties_dialog_folders,
+                                        this.mFolderUsage.getNumberOfFolders(),
+                                        Integer.valueOf(this.mFolderUsage.getNumberOfFolders()));
+            String files = res.getQuantityString(
+                                        R.plurals.fso_properties_dialog_files,
+                                        this.mFolderUsage.getNumberOfFiles(),
+                                        Integer.valueOf(this.mFolderUsage.getNumberOfFiles()));
+            final String contains = res.getString(
+                                        R.string.fso_properties_dialog_folder_items,
+                                        folders, files);
+
+            // Update the dialog
+            ((Activity)this.mContext).runOnUiThread(new Runnable() {
+                @Override
+                @SuppressWarnings("synthetic-access")
+                public void run() {
+                    if (computing) {
+                        FsoPropertiesDialog.this.mTvSize.setText(
+                                res.getString(R.string.computing_message, size));
+                        FsoPropertiesDialog.this.mTvContains.setText(
+                                res.getString(R.string.computing_message_ln, contains));
+                    } else {
+                        FsoPropertiesDialog.this.mTvSize.setText(size);
+                        FsoPropertiesDialog.this.mTvContains.setText(contains);
+                    }
+
+                    // End of drawing
+                    FsoPropertiesDialog.this.mDrawingFolderUsage = false;
+                }
+            });
+        }
+    }
+
 }
index 07e11bc..74d2f6b 100644 (file)
@@ -33,6 +33,7 @@ import com.cyanogenmod.explorer.commands.DiskUsageExecutable;
 import com.cyanogenmod.explorer.commands.EchoExecutable;
 import com.cyanogenmod.explorer.commands.Executable;
 import com.cyanogenmod.explorer.commands.FindExecutable;
+import com.cyanogenmod.explorer.commands.FolderUsageExecutable;
 import com.cyanogenmod.explorer.commands.GroupsExecutable;
 import com.cyanogenmod.explorer.commands.IdentityExecutable;
 import com.cyanogenmod.explorer.commands.ListExecutable;
@@ -102,8 +103,9 @@ public final class CommandHelper {
      * @see ChangeCurrentDirExecutable
      */
     public static boolean changeCurrentDir(Context context, String dst, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ChangeCurrentDirExecutable executable =
@@ -136,12 +138,14 @@ public final class CommandHelper {
      */
     public static boolean changeOwner(
             Context context, String src, User user, Group group, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         ChangeOwnerExecutable executable =
-                c.getExecutableFactory().newCreator().createChangeOwnerExecutable(src, user, group);
+                c.getExecutableFactory().
+                    newCreator().createChangeOwnerExecutable(src, user, group);
         writableExecute(context, executable, c);
         return executable.getResult().booleanValue();
     }
@@ -169,8 +173,9 @@ public final class CommandHelper {
      */
     public static boolean changePermissions(
             Context context, String src, Permissions permissions, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         ChangePermissionsExecutable executable =
@@ -201,8 +206,9 @@ public final class CommandHelper {
      * @see CreateDirExecutable
      */
     public static boolean createDirectory(Context context, String directory, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         CreateDirExecutable executable =
@@ -232,8 +238,9 @@ public final class CommandHelper {
      * @see CreateFileExecutable
      */
     public static boolean createFile(Context context, String file, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         CreateFileExecutable executable =
@@ -263,8 +270,9 @@ public final class CommandHelper {
      * @see DeleteDirExecutable
      */
     public static boolean deleteDirectory(Context context, String directory, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         DeleteDirExecutable executable =
@@ -294,8 +302,9 @@ public final class CommandHelper {
      * @see DeleteFileExecutable
      */
     public static boolean deleteFile(Context context, String file, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         DeleteFileExecutable executable =
@@ -324,8 +333,9 @@ public final class CommandHelper {
      * @see ResolveLinkExecutable
      */
     public static String getAbsolutePath(Context context, String path, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ResolveLinkExecutable executable =
@@ -358,8 +368,9 @@ public final class CommandHelper {
      * @see ResolveLinkExecutable
      */
     public static FileSystemObject resolveSymlink(Context context, String symlink, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ResolveLinkExecutable executable =
@@ -387,8 +398,9 @@ public final class CommandHelper {
      * @see CurrentDirExecutable
      */
     public static String getCurrentDir(Context context, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         CurrentDirExecutable executable =
@@ -417,12 +429,14 @@ public final class CommandHelper {
      * @see ListExecutable
      */
     public static FileSystemObject getFileInfo(Context context, String src, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ListExecutable executable =
-                c.getExecutableFactory().newCreator().createListExecutable(src, LIST_MODE.FILEINFO);
+                c.getExecutableFactory().
+                    newCreator().createListExecutable(src, LIST_MODE.FILEINFO);
         execute(context, executable, c);
         List<FileSystemObject> files = executable.getResult();
         if (files != null && files.size() > 0) {
@@ -450,8 +464,9 @@ public final class CommandHelper {
      * @see GroupsExecutable
      */
     public static List<Group> getGroups(Context context, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         GroupsExecutable executable =
@@ -479,8 +494,9 @@ public final class CommandHelper {
     * @see IdentityExecutable
     */
    public static Identity getIdentity(Context context, Console console)
-           throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-           InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+           throws FileNotFoundException, IOException, ConsoleAllocException,
+           NoSuchFileOrDirectory, InsufficientPermissionsException,
+           CommandNotFoundException, OperationTimeoutException,
            ExecutionException, InvalidCommandDefinitionException {
        Console c = ensureConsole(context, console);
        IdentityExecutable executable =
@@ -509,8 +525,9 @@ public final class CommandHelper {
      * @see ParentDirExecutable
      */
     public static String getParentDir(Context context, String src, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ParentDirExecutable executable =
@@ -540,11 +557,13 @@ public final class CommandHelper {
      * @see EchoExecutable
      */
     public static String getVariable(Context context, String msg, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
-        EchoExecutable executable = c.getExecutableFactory().newCreator().createEchoExecutable(msg);
+        EchoExecutable executable =
+                c.getExecutableFactory().newCreator().createEchoExecutable(msg);
         execute(context, executable, c);
         return executable.getResult();
     }
@@ -570,8 +589,9 @@ public final class CommandHelper {
      */
     public static List<FileSystemObject> listFiles(
             Context context, String directory, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ListExecutable executable =
@@ -603,8 +623,9 @@ public final class CommandHelper {
      * @see MoveExecutable
      */
     public static boolean move(Context context, String src, String dst, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         MoveExecutable executable =
@@ -635,8 +656,9 @@ public final class CommandHelper {
      * @see CopyExecutable
      */
     public static boolean copy(Context context, String src, String dst, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException {
         Console c = ensureConsole(context, console);
         CopyExecutable executable =
@@ -646,7 +668,7 @@ public final class CommandHelper {
     }
 
     /**
-     * Method that makes a search in a directory for search a term.
+     * Method that does a search in a directory tree seeking for some terms.
      *
      * @param context The current context (needed if console == null)
      * @param directory The "absolute" directory where start the search
@@ -670,8 +692,9 @@ public final class CommandHelper {
     public static AsyncResultExecutable findFiles(
             Context context, String directory, Query search,
             AsyncResultListener asyncResultListener, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         FindExecutable executable =
@@ -682,6 +705,42 @@ public final class CommandHelper {
     }
 
     /**
+     * Method that compute the disk usage of a folder.
+     *
+     * @param context The current context (needed if console == null)
+     * @param directory The "absolute" directory where start the search
+     * @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 AsyncResultProgram The command executed in background
+     * @throws FileNotFoundException If the initial directory not exists
+     * @throws IOException If initial directory can't not 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 "SearchResult"
+     * @see AsyncResultExecutable
+     */
+    public static AsyncResultExecutable getFolderUsage(
+            Context context, String directory,
+            AsyncResultListener asyncResultListener, Console console)
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
+            ExecutionException, InvalidCommandDefinitionException {
+        Console c = ensureConsole(context, console);
+        FolderUsageExecutable executable =
+                c.getExecutableFactory().newCreator().
+                    createFolderUsageExecutable(directory, asyncResultListener);
+        execute(context, executable, c);
+        return executable;
+    }
+
+    /**
      * Method that retrieves the disk usage of all the mount points.
      *
      * @param context The current context (needed if console == null)
@@ -700,8 +759,9 @@ public final class CommandHelper {
      * @see DiskUsageExecutable
      */
     public static List<DiskUsage> getDiskUsage(Context context, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         DiskUsageExecutable executable =
@@ -730,8 +790,9 @@ public final class CommandHelper {
      * @see DiskUsageExecutable
      */
     public static DiskUsage getDiskUsage(Context context, String dir, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         DiskUsageExecutable executable =
@@ -763,8 +824,9 @@ public final class CommandHelper {
      * @see MountPointInfoExecutable
      */
     public static List<MountPoint> getMountPoints(Context context, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         MountPointInfoExecutable executable =
@@ -794,8 +856,9 @@ public final class CommandHelper {
      * @see MountExecutable
      */
     public static boolean remount(Context context, MountPoint mp, boolean rw, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         MountExecutable executable =
@@ -824,8 +887,9 @@ public final class CommandHelper {
      * @see QuickFolderSearchExecutable
      */
     public static List<String> quickFolderSearch(Context context, String regexp, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         QuickFolderSearchExecutable executable =
@@ -839,6 +903,7 @@ public final class CommandHelper {
      * owned by the main process of this application).
      *
      * @param context The current context (needed if console == null)
+     * @param pid The process id of the shell where the command is running
      * @param processName The process name
      * @param console The console in which execute the program. <code>null</code>
      * to attach to the default console
@@ -854,13 +919,15 @@ public final class CommandHelper {
      * @throws ExecutionException If the operation returns a invalid exit code
      * @see ProcessIdExecutable
      */
-    public static Integer getProcessId(Context context, String processName, Console console)
-            throws FileNotFoundException, IOException, ConsoleAllocException, NoSuchFileOrDirectory,
-            InsufficientPermissionsException, CommandNotFoundException, OperationTimeoutException,
+    public static Integer getProcessId(
+            Context context, int pid, String processName, Console console)
+            throws FileNotFoundException, IOException, ConsoleAllocException,
+            NoSuchFileOrDirectory, InsufficientPermissionsException,
+            CommandNotFoundException, OperationTimeoutException,
             ExecutionException, InvalidCommandDefinitionException {
         Console c = ensureConsole(context, console);
         ProcessIdExecutable executable =
-                c.getExecutableFactory().newCreator().createProcessIdExecutable(processName);
+                c.getExecutableFactory().newCreator().createProcessIdExecutable(pid, processName);
         execute(context, executable, c);
         return executable.getResult();
     }
@@ -917,8 +984,8 @@ public final class CommandHelper {
         try {
             console.execute(executable);
         } catch (ReadOnlyFilesystemException rofEx) {
-            //ReadOnlyFilesystemException don't have sense if command is not writable
-            //WritableExecutable must be used with "writableExecute" method
+            // ReadOnlyFilesystemException don't have sense if command is not writable
+            // WritableExecutable must be used with "writableExecute" method
             throw new ExecutionException(rofEx.getMessage(), rofEx);
         }
     }
index 15291ee..6d43dc0 100644 (file)
@@ -193,12 +193,23 @@ public final class FileHelper {
      * if <code>fso</code> has no extension.
      */
     public static String getExtension(FileSystemObject fso) {
+        return getExtension(fso.getName());
+    }
+
+    /**
+     * Method that returns the extension of a file system object.
+     *
+     * @param fso The file system object
+     * @return The extension of the file system object, or <code>null</code>
+     * if <code>fso</code> has no extension.
+     */
+    public static String getExtension(String fso) {
         final char dot = '.';
-        int pos = fso.getName().lastIndexOf(dot);
+        int pos = fso.lastIndexOf(dot);
         if (pos == -1) {
             return null;
         }
-        return fso.getName().substring(pos + 1);
+        return fso.substring(pos + 1);
     }
 
     /**
index 903ca0f..127debb 100644 (file)
@@ -56,35 +56,35 @@ public final class MimeTypeHelper {
          */
         DOCUMENT,
         /**
-         * CD Image file 
+         * CD Image file
          */
         CDIMAGE,
         /**
-         * Compressed file 
+         * Compressed file
          */
         COMPRESS,
         /**
-         * Executable file 
+         * Executable file
          */
         EXEC,
         /**
-         * Database file 
+         * Database file
          */
         DATABASE,
         /**
-         * Image file 
+         * Image file
          */
         IMAGE,
         /**
-         * Audio file 
+         * Audio file
          */
         AUDIO,
         /**
-         * Video file 
+         * Video file
          */
         VIDEO,
         /**
-         * Security file (certificate, keys, ...) 
+         * Security file (certificate, keys, ...)
          */
         SECURITY
     }
@@ -150,12 +150,11 @@ public final class MimeTypeHelper {
 
         // Check if the fso is executable
         if (fso.getPermissions().getUser().isExecute()) {
-            return R.drawable.ic_fso_type_executable;
+            return R.drawable.fso_type_executable;
         }
         return R.drawable.ic_fso_default;
     }
 
-
     /**
      * Method that returns the mime/type description of the {@link FileSystemObject}.
      *
@@ -190,15 +189,20 @@ public final class MimeTypeHelper {
         }
         return res.getString(R.string.mime_unknown);
     }
-    
+
     /**
-     * Method that returns the mime/type category of the {@link FileSystemObject}
-     * 
+     * Method that returns the mime/type category of the file system object.
+     *
      * @param context The current context
      * @param fso The file system object
      * @return MimeTypeCategory The mime/type category
      */
     public static final MimeTypeCategory getCategory(Context context, FileSystemObject fso) {
+        // Ensure that have a context
+        if (context == null && sMimeTypes == null) {
+            // No category
+            return MimeTypeCategory.NONE;
+        }
         //Ensure that mime types are loaded
         if (sMimeTypes == null) {
             loadMimeTypes(context);
@@ -253,7 +257,7 @@ public final class MimeTypeHelper {
 
                     } catch (Exception e2) { /**NON BLOCK**/}
                 }
-                
+
             } catch (Exception e) {
                 Log.e(TAG, "Fail to load mime types raw file.", e); //$NON-NLS-1$
             }
index e8b9e2a..6d7024f 100644 (file)
@@ -67,6 +67,22 @@ public final class ParseHelper {
      * @param src The unix string style line
      * @return FileSystemObject The file system object reference
      * @throws ParseException If the line can't be parsed
+     * @see #toFileSystemObject(String, String, boolean)
+     */
+    public static FileSystemObject toFileSystemObject(
+            final String parent, final String src) throws ParseException {
+        return toFileSystemObject(parent, src, false);
+    }
+
+    /**
+     * Method that parses and creates a {@link FileSystemObject} references from
+     * a unix string style line.
+     *
+     * @param parent The parent of the object
+     * @param src The unix string style line
+     * @param quick Do not resolve data (User and Group doesn't have a valid reference)
+     * @return FileSystemObject The file system object reference
+     * @throws ParseException If the line can't be parsed
      */
     //
     //<permission> <user> <group> <size> <last modified>  <name>
@@ -119,7 +135,7 @@ public final class ParseHelper {
     //  - if object is a symlink, the value must be "link name -> real name"
     //
     public static FileSystemObject toFileSystemObject(
-            final String parent, final String src) throws ParseException {
+            final String parent, final String src, final boolean quick) throws ParseException {
 
         String raw = src;
 
@@ -146,17 +162,27 @@ public final class ParseHelper {
         String szStartLine = raw.substring(0, matcher.start()).trim();
         String szEndLine = raw.substring(matcher.end()).trim();
 
-        //3.- Extract user (user name has no spaces. Can ensure this?)
+        //3.- Extract user (user name has no spaces.
         int pos = szStartLine.indexOf(" "); //$NON-NLS-1$
         String szUser = szStartLine.substring(0, pos).trim();
         szStartLine = szStartLine.substring(pos).trim();
-        User oUser = new User(Process.getUidForName(szUser), szUser);
+        User oUser = null;
+        if (!quick) {
+            oUser = new User(Process.getUidForName(szUser), szUser);
+        } else {
+            oUser = new User(-1, szUser);
+        }
 
-        //4.- Extract group (group name has no spaces. Can ensure this?)
+        //4.- Extract group (group name has no spaces.
         pos = szStartLine.indexOf(" "); //$NON-NLS-1$
         String szGroup = szStartLine.substring(0, (pos == -1) ? szStartLine.length() : pos).trim();
         szStartLine = szStartLine.substring((pos == -1) ? szStartLine.length() : pos).trim();
-        Group oGroup = new Group(Process.getGidForName(szGroup), szGroup);
+        Group oGroup = null;
+        if (!quick) {
+            oGroup = new Group(Process.getGidForName(szGroup), szGroup);
+        } else {
+            oGroup = new Group(-1, szGroup);
+        }
 
         //5.- Extract size
         long lSize = 0;
@@ -360,24 +386,24 @@ public final class ParseHelper {
      * Method that converts to bytes the string representation
      * of a size (10M, 1G, 0K, ...).
      *
-     * @param szSize The size as a string representation
+     * @param size The size as a string representation
      * @return long The size in bytes
      */
-    private static long toBytes(String szSize) {
-        long size = Long.parseLong(szSize.substring(0, szSize.length() - 1));
-        String unit = szSize.substring(szSize.length() - 1);
+    private static long toBytes(String size) {
+        long bytes = Long.parseLong(size.substring(0, size.length() - 1));
+        String unit = size.substring(size.length() - 1);
         if (unit.compareToIgnoreCase("G") == 0) { //$NON-NLS-1$
-            return size * 1024 * 1024 * 1024;
+            return bytes * 1024 * 1024 * 1024;
         }
         if (unit.compareToIgnoreCase("M") == 0) { //$NON-NLS-1$
-            return size * 1024 * 1024;
+            return bytes * 1024 * 1024;
         }
         if (unit.compareToIgnoreCase("K") == 0) { //$NON-NLS-1$
-            return size * 1024;
+            return bytes * 1024;
         }
 
         //Don't touch
-        return size;
+        return bytes;
     }
 
 }
index 39b1415..1fd0d88 100644 (file)
@@ -4,6 +4,8 @@
 
   <original-package android:name="com.cyanogenmod.explorer.test" />
 
+  <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
 
index 870121d..664e141 100644 (file)
@@ -51,7 +51,7 @@ public class FindCommandTest extends AbstractConsoleTest {
      * @throws Exception If test failed
      */
     @SuppressWarnings("synthetic-access")
-    public void testListWithPartialResult() throws Exception {
+    public void testFindWithPartialResult() throws Exception {
         this.mNewPartialData = false;
         Query query = new Query().setSlot(FIND_TERM_PARTIAL, 0);
         final List<FileSystemObject> files = new ArrayList<FileSystemObject>();
@@ -67,9 +67,10 @@ public class FindCommandTest extends AbstractConsoleTest {
                         public void onException(Exception cause) {
                             fail(cause.toString());
                         }
-                        public void onPartialResult(List<FileSystemObject> results) {
+                        @SuppressWarnings("unchecked")
+                        public void onPartialResult(Object results) {
                             FindCommandTest.this.mNewPartialData = true;
-                            files.addAll(results);
+                            files.addAll((List<FileSystemObject>)results);
                         }
                    }, getConsole());
         synchronized (FindCommandTest.this.mSync) {
diff --git a/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java b/tests/src/com/cyanogenmod/explorer/commands/shell/FolderUsageCommandTest.java
new file mode 100644 (file)
index 0000000..29b4354
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.explorer.commands.shell;
+
+import android.util.Log;
+
+import com.cyanogenmod.explorer.commands.AsyncResultListener;
+import com.cyanogenmod.explorer.model.FolderUsage;
+import com.cyanogenmod.explorer.util.CommandHelper;
+import com.cyanogenmod.explorer.util.MimeTypeHelper.MimeTypeCategory;
+
+/**
+ * A class for testing folder usage command.
+ *
+ * @see FindCommand
+ */
+public class FolderUsageCommandTest extends AbstractConsoleTest {
+
+    private static final String TAG = "FolderUsageCommandTest"; //$NON-NLS-1$
+
+    private static final String PATH = "/system"; //$NON-NLS-1$
+
+    private final Object mSync = new Object();
+    private boolean mNewPartialData;
+    private FolderUsage mUsage;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRootConsoleNeeded() {
+        return false;
+    }
+
+    /**
+     * Method that performs a test over known search results.
+     *
+     * @throws Exception If test failed
+     */
+    @SuppressWarnings("synthetic-access")
+    public void testFolderUsageWithPartialResult() throws Exception {
+        this.mNewPartialData = false;
+        this.mUsage = null;
+        CommandHelper.getFolderUsage(getContext(), PATH, new AsyncResultListener() {
+                        public void onAsyncStart() {
+                            /**NON BLOCK**/
+                        }
+                        public void onAsyncEnd(boolean cancelled) {
+                            synchronized (FolderUsageCommandTest.this.mSync) {
+                                FolderUsageCommandTest.this.mSync.notifyAll();
+                            }
+                        }
+                        public void onException(Exception cause) {
+                            fail(cause.toString());
+                        }
+                        public void onPartialResult(Object result) {
+                            FolderUsageCommandTest.this.mNewPartialData = true;
+                            try {
+                                FolderUsageCommandTest.this.mUsage =
+                                        (FolderUsage)(((FolderUsage)result).clone());
+                            } catch (Exception e) {/**NON BLOCK**/}
+                            Log.d(TAG, FolderUsageCommandTest.this.mUsage.toString());
+                        }
+                   }, getConsole());
+        synchronized (FolderUsageCommandTest.this.mSync) {
+            FolderUsageCommandTest.this.mSync.wait(25000L);
+        }
+        assertTrue("no new partial data", this.mNewPartialData); //$NON-NLS-1$
+        assertNotNull("usage==null", this.mUsage); //$NON-NLS-1$
+        assertTrue("no folder returned", this.mUsage.getNumberOfFolders() > 0); //$NON-NLS-1$
+        assertTrue("no files returned", this.mUsage.getNumberOfFiles() > 0); //$NON-NLS-1$
+        assertTrue("no size returned", this.mUsage.getTotalSize() > 0); //$NON-NLS-1$
+        assertTrue("no text category returned", //$NON-NLS-1$
+                this.mUsage.getStatisticsForCategory(MimeTypeCategory.TEXT) > 0);
+    }
+
+}
index bdabcc5..6196690 100644 (file)
  */
 
 package com.cyanogenmod.explorer.commands.shell;
-
-import com.cyanogenmod.explorer.ExplorerApplication;
-import com.cyanogenmod.explorer.util.CommandHelper;
-
 /**
  * A class for testing the {@link ProcessIdCommand} command.
  *
@@ -34,21 +30,6 @@ public class ProcessIdCommandTest extends AbstractConsoleTest {
         return false;
     }
 
-    /**
-     * Method that performs a test over id command.
-     *
-     * @throws Exception If test failed
-     */
-    public void testId() throws Exception {
-        Integer main = Integer.valueOf(android.os.Process.myPid());
-        Integer pid =
-                CommandHelper.getProcessId(
-                        getContext(),
-                        ExplorerApplication.MAIN_PROCESS, getConsole());
-        assertNotNull("pid==null", pid); //$NON-NLS-1$
-        assertTrue(
-                String.format("pid != main process id (%d)", main),  //$NON-NLS-1$
-                pid.compareTo(main) == 0);
-    }
-
+    // Can't perform any test, because a running program in a shell is needed, and PID of
+    // shell is not available for external use outside the console.
 }