diff --git a/.gitignore b/.gitignore
index 812ec5ac9..bf4761ab1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,42 @@
-/.settings
-/bin
-/gen
-/.checkstyle
-/.classpath
-/.project
-/lint.xml
-/project.properties
+#Android generated
+bin
+gen
+releases
+lint.xml
+
+#Eclipse
+.project
+.classpath
+.settings
+.checkstyle
+
+#IntelliJ IDEA
+.idea/
+*.iml
+*.ipr
+*.iws
+classes
+gen-external-apklibs
+
+#Maven
+target
+release.properties
+pom.xml.*
+
+#Ant
+build.xml
+ant.properties
+local.properties
+proguard.cfg
+proguard-project.txt
+
+#Gradle
+build/
+.gradle
+gradle.properties
+
+#Other
+.DS_Store
+tmp
+misc
+out/
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 1fafe2a8f..000000000
--- a/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := CMFileManager
-LOCAL_CERTIFICATE := platform
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644
index b7c37b486..000000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,183 +0,0 @@
-
-
-
-ColorDialogView
+ *
+ * @param context The current context
+ */
+ public ColorDialogView(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Constructor of ColorDialogView
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ */
+ public ColorDialogView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * Constructor of ColorDialogView
+ *
+ * @param context The current context
+ * @param attrs The attributes of the XML tag that is inflating the view.
+ * @param defStyle The default style to apply to this view. If 0, no style
+ * will be applied (beyond what is included in the theme). This may
+ * either be an attribute resource, whose value will be retrieved
+ * from the current theme, or an explicit style resource.
+ */
+ public ColorDialogView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ /**
+ * Method that initializes the view. This method loads all the necessary
+ * information and create an appropriate layout for the view
+ */
+ private void init() {
+ // To fight color branding.
+ ((Activity)getContext()).getWindow().setFormat(PixelFormat.RGBA_8888);
+
+ // Create the scrollview over the dialog
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ ScrollView sv = new ScrollView(getContext());
+ sv.setId(generateViewId());
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sv.setLayoutParams(lp);
+ sv.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
+
+ // Now the vertical layout
+ LinearLayout ll = new LinearLayout(getContext());
+ ll.setId(generateViewId());
+ lp = new RelativeLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT);
+ ll.setLayoutParams(lp);
+ ll.setOrientation(LinearLayout.VERTICAL);
+ sv.addView(ll);
+
+ // Creates the color input field
+ int id = createColorInput(ll);
+
+ // Creates the color picker
+ id = createColorPicker(ll, id);
+
+ // Creates the current color and new color panels
+ id = createColorsPanel(ll, id);
+
+ // Add the scrollview
+ addView(sv);
+
+ // Sets the input color
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ }
+
+ /**
+ * Method that creates the color input
+ *
+ * @param parent The parent layout
+ */
+ private int createColorInput(ViewGroup parent) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT);
+ lp2.setMargins(0, 0, dlgMarging, 0);
+ this.tvColorLabel = new TextView(getContext());
+ this.tvColorLabel.setText(this.mColorLabelText);
+ this.tvColorLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_LABEL_TEXT_SIZE_SP);
+ this.tvColorLabel.setGravity(Gravity.BOTTOM | Gravity.LEFT);
+ this.tvColorLabel.setLayoutParams(lp2);
+
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ this.etColor = new EditText(getContext());
+ this.etColor.setSingleLine();
+ this.etColor.setGravity(Gravity.TOP | Gravity.LEFT);
+ this.etColor.setCursorVisible(true);
+ this.etColor.setImeOptions(
+ EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_FULLSCREEN);
+ this.etColor.setInputType(
+ InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+ this.etColor.setLayoutParams(lp2);
+ InputFilter[] filters = new InputFilter[2];
+ filters[0] = new InputFilter.LengthFilter(8);
+ filters[1] = new InputFilter() {
+ @Override
+ public CharSequence filter(CharSequence source, int start,
+ int end, Spanned dest, int dstart, int dend) {
+ if (start >= end) return ""; //$NON-NLS-1$
+ String s = source.subSequence(start, end).toString();
+ StringBuilder sb = new StringBuilder();
+ int cc = s.length();
+ for (int i = 0; i < cc; i++) {
+ char c = s.charAt(i);
+ if ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'f') ||
+ (c >= 'A' && c <= 'F')) {
+ sb.append(c);
+ }
+ }
+ return sb.toString().toUpperCase();
+ }
+ };
+ this.etColor.setFilters(filters);
+ this.etColor.addTextChangedListener(this);
+
+ LinearLayout ll1 = new LinearLayout(getContext());
+ ll1.setId(generateViewId());
+ LayoutParams lp = new LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ ll1.setLayoutParams(lp);
+ ll1.addView(this.tvColorLabel);
+ ll1.addView(this.etColor);
+ parent.addView(ll1);
+
+ return ll1.getId();
+ }
+
+ /**
+ * Method that creates the color picker
+ *
+ * @param parent The parent layout
+ * @param belowOf The anchor view
+ * @return id The layout id
+ */
+ private int createColorPicker(ViewGroup parent, int belowOf) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ this.mPickerView = new ColorPickerView(getContext());
+ this.mPickerView.setId(generateViewId());
+ this.mPickerView.setOnColorChangedListener(this);
+ LayoutParams lp = new LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, 0);
+ lp.addRule(RelativeLayout.BELOW, belowOf);
+ this.mPickerView.setLayoutParams(lp);
+ parent.addView(this.mPickerView);
+ return this.mPickerView.getId();
+ }
+
+ /**
+ * Method that creates the colors panel (current and new)
+ *
+ * @param parent The parent layout
+ * @param belowOf The anchor view
+ * @return id The layout id
+ */
+ private int createColorsPanel(ViewGroup parent, int belowOf) {
+ final int dlgMarging = (int)convertDpToPixel(DEFAULT_MARGIN_DP);
+ final int panelHeight = (int)convertDpToPixel(DEFAULT_PANEL_HEIGHT_DP);
+ LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ 1);
+
+ // Titles
+ this.tvCurrent = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1);
+ this.tvCurrent.setLayoutParams(lp2);
+ this.tvCurrent.setText(this.mCurrentLabelText);
+ this.tvCurrent.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+ this.tvNew = new TextView(getContext());
+ this.tvNew.setLayoutParams(lp2);
+ this.tvNew.setText(this.mNewLabelText);
+ this.tvNew.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+ TextView sep1 = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0);
+ lp2.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sep1.setLayoutParams(lp2);
+ sep1.setText(" "); //$NON-NLS-1$
+ sep1.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+
+ LinearLayout ll1 = new LinearLayout(getContext());
+ ll1.setId(generateViewId());
+ LayoutParams lp = new LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
+ lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2);
+ lp.addRule(RelativeLayout.BELOW, belowOf);
+ ll1.setLayoutParams(lp);
+ ll1.addView(this.tvCurrent);
+ ll1.addView(sep1);
+ ll1.addView(this.tvNew);
+ parent.addView(ll1);
+
+ // Color panels
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 1);
+ this.mCurrentColorView = new ColorPanelView(getContext());
+ this.mCurrentColorView.setLayoutParams(lp2);
+ this.mNewColorView = new ColorPanelView(getContext());
+ this.mNewColorView.setLayoutParams(lp2);
+ TextView sep2 = new TextView(getContext());
+ lp2 = new LinearLayout.LayoutParams(
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
+ 0);
+ lp2.setMargins(dlgMarging, 0, dlgMarging, 0);
+ sep2.setLayoutParams(lp2);
+ sep2.setText("-"); //$NON-NLS-1$
+ sep2.setTextSize(TypedValue.COMPLEX_UNIT_SP, DEFAULT_TEXT_SIZE_SP);
+
+ LinearLayout ll2 = new LinearLayout(getContext());
+ ll2.setId(generateViewId());
+ lp = new LayoutParams(
+ android.view.ViewGroup.LayoutParams.MATCH_PARENT, panelHeight);
+ lp.setMargins(dlgMarging, 0, dlgMarging, dlgMarging/2);
+ lp.addRule(RelativeLayout.BELOW, ll1.getId());
+ ll2.setLayoutParams(lp);
+ ll2.addView(this.mCurrentColorView);
+ ll2.addView(sep2);
+ ll2.addView(this.mNewColorView);
+ parent.addView(ll2);
+
+ return ll2.getId();
+ }
+
+ /**
+ * Method that returns the color of the picker
+ *
+ * @return The ARGB color
+ */
+ public int getColor() {
+ return this.mPickerView.getColor();
+ }
+
+ /**
+ * Method that set the color of the picker
+ *
+ * @param argb The ARGB color
+ */
+ public void setColor(int argb) {
+ setColor(argb, false);
+ }
+
+ /**
+ * Method that set the color of the picker
+ *
+ * @param argb The ARGB color
+ * @param fromEditText If the call comes from the EditText
+ */
+ private void setColor(int argb, boolean fromEditText) {
+ this.mPickerView.setColor(argb, false);
+ this.mCurrentColorView.setColor(argb);
+ this.mNewColorView.setColor(argb);
+ if (!fromEditText) {
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ }
+ }
+
+ /**
+ * Method that display/hide the alpha slider
+ *
+ * @param show If the alpha slider should be shown
+ */
+ public void showAlphaSlider(boolean show) {
+ this.mPickerView.setAlphaSliderVisible(show);
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ this.mPickerView.setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the actual color panel.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setCurrentColorText(String text) {
+ this.mCurrentLabelText = text;
+ this.tvCurrent.setText(this.mCurrentLabelText);
+ }
+
+ /**
+ * Set the text that should be shown in the new color panel.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setNewColorText(String text) {
+ this.mNewLabelText = text;
+ this.tvNew.setText(this.mNewLabelText);
+ }
+
+ /**
+ * Set the text that should be shown in the label of the color input.
+ * Set to null to disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setColorLabelText(String text) {
+ this.mColorLabelText = text;
+ this.tvColorLabel.setText(this.mColorLabelText);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onColorChanged(int color) {
+ this.mNewColorView.setColor(color);
+ this.etColor.removeTextChangedListener(this);
+ this.etColor.setText(toHex(this.mNewColorView.getColor()));
+ this.etColor.addTextChangedListener(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() == 8) {
+ try {
+ setColor(toARGB(s.toString()), true);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * This method converts dp unit to equivalent device specific value in pixels.
+ *
+ * @param ctx The current context
+ * @param dp A value in dp (Device independent pixels) unit
+ * @return float A float value to represent Pixels equivalent to dp according to device
+ */
+ private float convertDpToPixel(float dp) {
+ Resources resources = getContext().getResources();
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+ return dp * (metrics.densityDpi / 160f);
+ }
+
+ /**
+ * Method that converts an ARGB color to its hex string color representation
+ *
+ * @param argb The ARGB color
+ * @return String The hex string representation of the color
+ */
+ private static String toHex(int argb) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(toHexString((byte)Color.alpha(argb)));
+ sb.append(toHexString((byte)Color.red(argb)));
+ sb.append(toHexString((byte)Color.green(argb)));
+ sb.append(toHexString((byte)Color.blue(argb)));
+ return sb.toString();
+ }
+
+ /**
+ * Method that converts an hex string color representation to an ARGB color
+ *
+ * @param hex The hex string representation of the color
+ * @return int The ARGB color
+ */
+ private static int toARGB(String hex) {
+ return Color.parseColor("#" + hex); //$NON-NLS-1$
+ }
+
+ /**
+ * Method that converts a byte into its hex string representation
+ *
+ * @param v The value to convert
+ * @return String The hex string representation
+ */
+ private static String toHexString(byte v) {
+ String hex = Integer.toHexString(v & 0xff);
+ if (hex.length() == 1) {
+ hex = "0" + hex; //$NON-NLS-1$
+ }
+ return hex.toUpperCase();
+ }
+}
diff --git a/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPanelView.java b/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPanelView.java
new file mode 100644
index 000000000..51884aa5a
--- /dev/null
+++ b/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPanelView.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ *
+ * 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.afzkl.development.mColorPicker.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import com.afzkl.development.mColorPicker.drawables.AlphaPatternDrawable;
+
+/**
+ * This class draws a panel which which will be filled with a color which can be set.
+ * It can be used to show the currently selected color which you will get from
+ * the {@link com.afzkl.development.mColorPicker.views.ColorPickerView}.
+ * @author Daniel Nilsson
+ *
+ */
+@SuppressWarnings("all")
+public class ColorPanelView extends View{
+
+ /**
+ * The width in pixels of the border
+ * surrounding the color panel.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ private static float mDensity = 1f;
+
+ private int mBorderColor = 0xff6E6E6E;
+ private int mColor = 0xff000000;
+
+ private Paint mBorderPaint;
+ private Paint mColorPaint;
+
+ private RectF mDrawingRect;
+ private RectF mColorRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+
+ public ColorPanelView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init();
+ }
+
+ private void init() {
+ mBorderPaint = new Paint();
+ mColorPaint = new Paint();
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ final RectF rect = mColorRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect, mBorderPaint);
+ }
+
+ if (mAlphaPattern != null) {
+ mAlphaPattern.draw(canvas);
+ }
+
+ mColorPaint.setColor(mColor);
+
+ canvas.drawRect(rect, mColorPaint);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = getPaddingLeft();
+ mDrawingRect.right = w - getPaddingRight();
+ mDrawingRect.top = getPaddingTop();
+ mDrawingRect.bottom = h - getPaddingBottom();
+
+ setUpColorRect();
+
+ }
+
+ private void setUpColorRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mColorRect = new RectF(left,top, right, bottom);
+
+ mAlphaPattern = new AlphaPatternDrawable((int)(5 * mDensity));
+
+ mAlphaPattern.setBounds(Math.round(mColorRect.left),
+ Math.round(mColorRect.top),
+ Math.round(mColorRect.right),
+ Math.round(mColorRect.bottom));
+
+ }
+
+ /**
+ * Set the color that should be shown by this view.
+ * @param color
+ */
+ public void setColor(int color) {
+ mColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color currently show by this view.
+ * @return
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Set the color of the border surrounding the panel.
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding the panel.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+}
diff --git a/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPickerView.java b/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPickerView.java
new file mode 100644
index 000000000..c2808625b
--- /dev/null
+++ b/Backbone/src/main/java/com/afzkl/development/mColorPicker/views/ColorPickerView.java
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ *
+ * 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.afzkl.development.mColorPicker.views;
+
+import android.content.Context;
+import android.graphics.*;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Shader.TileMode;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import com.afzkl.development.mColorPicker.drawables.AlphaPatternDrawable;
+
+import java.lang.reflect.Method;
+
+/**
+ * Displays a color picker to the user and allow them
+ * to select a color. A slider for the alpha channel is
+ * also available. Enable it by setting
+ * setAlphaSliderVisible(boolean) to true.
+ * @author Daniel Nilsson
+ */
+@SuppressWarnings("all")
+public class ColorPickerView extends View{
+
+ public interface OnColorChangedListener{
+ public void onColorChanged(int color);
+ }
+
+ private final static int PANEL_SAT_VAL = 0;
+ private final static int PANEL_HUE = 1;
+ private final static int PANEL_ALPHA = 2;
+
+ /**
+ * The width in pixels of the border
+ * surrounding all color panels.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ /**
+ * The width in dp of the hue panel.
+ */
+ private float HUE_PANEL_WIDTH = 30f;
+ /**
+ * The height in dp of the alpha panel
+ */
+ private float ALPHA_PANEL_HEIGHT = 20f;
+ /**
+ * The distance in dp between the different
+ * color panels.
+ */
+ private float PANEL_SPACING = 10f;
+ /**
+ * The radius in dp of the color palette tracker circle.
+ */
+ private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f;
+ /**
+ * The dp which the tracker of the hue or alpha panel
+ * will extend outside of its bounds.
+ */
+ private float RECTANGLE_TRACKER_OFFSET = 2f;
+
+
+ private static float mDensity = 1f;
+
+ private OnColorChangedListener mListener;
+
+ private Paint mSatValPaint;
+ private Paint mSatValTrackerPaint;
+
+ private Paint mHuePaint;
+ private Paint mHueTrackerPaint;
+
+ private Paint mAlphaPaint;
+ private Paint mAlphaTextPaint;
+
+ private Paint mBorderPaint;
+
+ private Shader mValShader;
+ private Shader mSatShader;
+ private Shader mHueShader;
+ private Shader mAlphaShader;
+
+ private int mAlpha = 0xff;
+ private float mHue = 360f;
+ private float mSat = 0f;
+ private float mVal = 0f;
+
+ private String mAlphaSliderText = "Alpha";
+ private int mSliderTrackerColor = 0xff1c1c1c;
+ private int mBorderColor = 0xff6E6E6E;
+ private boolean mShowAlphaPanel = false;
+
+ /*
+ * To remember which panel that has the "focus" when
+ * processing hardware button data.
+ */
+ private int mLastTouchedPanel = PANEL_SAT_VAL;
+
+ /**
+ * Offset from the edge we must have or else
+ * the finger tracker will get clipped when
+ * it is drawn outside of the view.
+ */
+ private float mDrawingOffset;
+
+
+ /*
+ * Distance form the edges of the view
+ * of where we are allowed to draw.
+ */
+ private RectF mDrawingRect;
+
+ private RectF mSatValRect;
+ private RectF mHueRect;
+ private RectF mAlphaRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ private Point mStartTouchPoint = null;
+
+
+ public ColorPickerView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity;
+ RECTANGLE_TRACKER_OFFSET *= mDensity;
+ HUE_PANEL_WIDTH *= mDensity;
+ ALPHA_PANEL_HEIGHT *= mDensity;
+ PANEL_SPACING = PANEL_SPACING * mDensity;
+
+ mDrawingOffset = calculateRequiredOffset();
+
+ initPaintTools();
+
+ //Needed for receiving trackball motion events.
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ }
+
+ private void initPaintTools() {
+
+ mSatValPaint = new Paint();
+ mSatValTrackerPaint = new Paint();
+ mHuePaint = new Paint();
+ mHueTrackerPaint = new Paint();
+ mAlphaPaint = new Paint();
+ mAlphaTextPaint = new Paint();
+ mBorderPaint = new Paint();
+
+
+ mSatValTrackerPaint.setStyle(Style.STROKE);
+ mSatValTrackerPaint.setStrokeWidth(2f * mDensity);
+ mSatValTrackerPaint.setAntiAlias(true);
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ mHueTrackerPaint.setStyle(Style.STROKE);
+ mHueTrackerPaint.setStrokeWidth(2f * mDensity);
+ mHueTrackerPaint.setAntiAlias(true);
+
+ mAlphaTextPaint.setColor(0xff1c1c1c);
+ mAlphaTextPaint.setTextSize(14f * mDensity);
+ mAlphaTextPaint.setAntiAlias(true);
+ mAlphaTextPaint.setTextAlign(Align.CENTER);
+ mAlphaTextPaint.setFakeBoldText(true);
+
+
+ }
+
+ private float calculateRequiredOffset() {
+ float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET);
+ offset = Math.max(offset, BORDER_WIDTH_PX * mDensity);
+
+ return offset * 1.5f;
+ }
+
+ private int[] buildHueColorArray() {
+
+ int[] hue = new int[361];
+
+ int count = 0;
+ for (int i = hue.length -1; i >= 0; i--, count++) {
+ hue[count] = Color.HSVToColor(new float[]{i, 1f, 1f});
+ }
+
+ return hue;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ checkHardwareAccelerationSupport();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) return;
+
+ drawSatValPanel(canvas);
+ drawHuePanel(canvas);
+ drawAlphaPanel(canvas);
+
+ }
+
+ private void drawSatValPanel(Canvas canvas) {
+
+ final RectF rect = mSatValRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX, rect.bottom + BORDER_WIDTH_PX, mBorderPaint);
+ }
+
+ if (mValShader == null) {
+ mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ 0xffffffff, 0xff000000, TileMode.CLAMP);
+ }
+
+ int rgb = Color.HSVToColor(new float[]{mHue,1f,1f});
+
+ mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ 0xffffffff, rgb, TileMode.CLAMP);
+ ComposeShader mShader = new ComposeShader(mValShader, mSatShader, PorterDuff.Mode.MULTIPLY);
+ mSatValPaint.setShader(mShader);
+
+ canvas.drawRect(rect, mSatValPaint);
+
+ Point p = satValToPoint(mSat, mVal);
+
+ mSatValTrackerPaint.setColor(0xff000000);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity, mSatValTrackerPaint);
+
+ mSatValTrackerPaint.setColor(0xffdddddd);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint);
+
+ }
+
+ private void drawHuePanel(Canvas canvas) {
+
+ final RectF rect = mHueRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ if (mHueShader == null) {
+ mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom, buildHueColorArray(), null, TileMode.CLAMP);
+ mHuePaint.setShader(mHueShader);
+ }
+
+ canvas.drawRect(rect, mHuePaint);
+
+ float rectHeight = 4 * mDensity / 2;
+
+ Point p = hueToPoint(mHue);
+
+ RectF r = new RectF();
+ r.left = rect.left - RECTANGLE_TRACKER_OFFSET;
+ r.right = rect.right + RECTANGLE_TRACKER_OFFSET;
+ r.top = p.y - rectHeight;
+ r.bottom = p.y + rectHeight;
+
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+ private void drawAlphaPanel(Canvas canvas) {
+
+ if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) return;
+
+ final RectF rect = mAlphaRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+
+ mAlphaPattern.draw(canvas);
+
+ float[] hsv = new float[]{mHue,mSat,mVal};
+ int color = Color.HSVToColor(hsv);
+ int acolor = Color.HSVToColor(0, hsv);
+
+ mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ color, acolor, TileMode.CLAMP);
+
+
+ mAlphaPaint.setShader(mAlphaShader);
+
+ canvas.drawRect(rect, mAlphaPaint);
+
+ if (mAlphaSliderText != null && mAlphaSliderText!= "") {
+ canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity, mAlphaTextPaint);
+ }
+
+ float rectWidth = 4 * mDensity / 2;
+
+ Point p = alphaToPoint(mAlpha);
+
+ RectF r = new RectF();
+ r.left = p.x - rectWidth;
+ r.right = p.x + rectWidth;
+ r.top = rect.top - RECTANGLE_TRACKER_OFFSET;
+ r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+
+ private Point hueToPoint(float hue) {
+
+ final RectF rect = mHueRect;
+ final float height = rect.height();
+
+ Point p = new Point();
+
+ p.y = (int) (height - (hue * height / 360f) + rect.top);
+ p.x = (int) rect.left;
+
+ return p;
+ }
+
+ private Point satValToPoint(float sat, float val) {
+
+ final RectF rect = mSatValRect;
+ final float height = rect.height();
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (sat * width + rect.left);
+ p.y = (int) ((1f - val) * height + rect.top);
+
+ return p;
+ }
+
+ private Point alphaToPoint(int alpha) {
+
+ final RectF rect = mAlphaRect;
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (width - (alpha * width / 0xff) + rect.left);
+ p.y = (int) rect.top;
+
+ return p;
+
+ }
+
+ private float[] pointToSatVal(float x, float y) {
+
+ final RectF rect = mSatValRect;
+ float[] result = new float[2];
+
+ float width = rect.width();
+ float height = rect.height();
+
+ if (x < rect.left) {
+ x = 0f;
+ }
+ else if (x > rect.right) {
+ x = width;
+ }
+ else{
+ x = x - rect.left;
+ }
+
+ if (y < rect.top) {
+ y = 0f;
+ }
+ else if (y > rect.bottom) {
+ y = height;
+ }
+ else{
+ y = y - rect.top;
+ }
+
+
+ result[0] = 1.f / width * x;
+ result[1] = 1.f - (1.f / height * y);
+
+ return result;
+ }
+
+ private float pointToHue(float y) {
+
+ final RectF rect = mHueRect;
+
+ float height = rect.height();
+
+ if (y < rect.top) {
+ y = 0f;
+ }
+ else if (y > rect.bottom) {
+ y = height;
+ }
+ else{
+ y = y - rect.top;
+ }
+
+ return 360f - (y * 360f / height);
+ }
+
+ private int pointToAlpha(int x) {
+
+ final RectF rect = mAlphaRect;
+ final int width = (int) rect.width();
+
+ if (x < rect.left) {
+ x = 0;
+ }
+ else if (x > rect.right) {
+ x = width;
+ }
+ else{
+ x = x - (int)rect.left;
+ }
+
+ return 0xff - (x * 0xff / width);
+
+ }
+
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+
+ float x = event.getX();
+ float y = event.getY();
+
+ boolean update = false;
+
+
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+
+ switch(mLastTouchedPanel) {
+
+ case PANEL_SAT_VAL:
+
+ float sat, val;
+
+ sat = mSat + x/50f;
+ val = mVal - y/50f;
+
+ if (sat < 0f) {
+ sat = 0f;
+ }
+ else if (sat > 1f) {
+ sat = 1f;
+ }
+
+ if (val < 0f) {
+ val = 0f;
+ }
+ else if (val > 1f) {
+ val = 1f;
+ }
+
+ mSat = sat;
+ mVal = val;
+
+ update = true;
+
+ break;
+
+ case PANEL_HUE:
+
+ float hue = mHue - y * 10f;
+
+ if (hue < 0f) {
+ hue = 0f;
+ }
+ else if (hue > 360f) {
+ hue = 360f;
+ }
+
+ mHue = hue;
+
+ update = true;
+
+ break;
+
+ case PANEL_ALPHA:
+
+ if (!mShowAlphaPanel || mAlphaRect == null) {
+ update = false;
+ }
+ else{
+
+ int alpha = (int) (mAlpha - x*10);
+
+ if (alpha < 0) {
+ alpha = 0;
+ }
+ else if (alpha > 0xff) {
+ alpha = 0xff;
+ }
+
+ mAlpha = alpha;
+
+
+ update = true;
+ }
+
+ break;
+ }
+
+
+ }
+
+
+ if (update) {
+
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ return true;
+ }
+
+
+ return super.onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ boolean update = false;
+
+ switch(event.getAction()) {
+
+ case MotionEvent.ACTION_DOWN:
+
+ mStartTouchPoint = new Point((int)event.getX(), (int)event.getY());
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ case MotionEvent.ACTION_UP:
+
+ mStartTouchPoint = null;
+
+ update = moveTrackersIfNeeded(event);
+
+ break;
+
+ }
+
+ if (update) {
+
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ return true;
+ }
+
+
+ return super.onTouchEvent(event);
+ }
+
+ private boolean moveTrackersIfNeeded(MotionEvent event) {
+
+ if (mStartTouchPoint == null) return false;
+
+ boolean update = false;
+
+ int startX = mStartTouchPoint.x;
+ int startY = mStartTouchPoint.y;
+
+
+ if (mHueRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_HUE;
+
+ mHue = pointToHue(event.getY());
+
+ update = true;
+ }
+ else if (mSatValRect.contains(startX, startY)) {
+
+ mLastTouchedPanel = PANEL_SAT_VAL;
+
+ float[] result = pointToSatVal(event.getX(), event.getY());
+
+ mSat = result[0];
+ mVal = result[1];
+
+ update = true;
+ }
+ else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) {
+
+ mLastTouchedPanel = PANEL_ALPHA;
+
+ mAlpha = pointToAlpha((int)event.getX());
+
+ update = true;
+ }
+
+
+ return update;
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = 0;
+ int height = 0;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ int widthAllowed = MeasureSpec.getSize(widthMeasureSpec);
+ int heightAllowed = MeasureSpec.getSize(heightMeasureSpec);
+
+
+ widthAllowed = chooseWidth(widthMode, widthAllowed);
+ heightAllowed = chooseHeight(heightMode, heightAllowed);
+
+
+ if (!mShowAlphaPanel) {
+ height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH);
+
+ //If calculated height (based on the width) is more than the allowed height.
+ if (height > heightAllowed) {
+ height = heightAllowed;
+ width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH);
+ }
+ else{
+ width = widthAllowed;
+ }
+ }
+ else{
+
+ width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH);
+
+ if (width > widthAllowed) {
+ width = widthAllowed;
+ height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT);
+ }
+ else{
+ height = heightAllowed;
+ }
+
+
+ }
+
+
+ setMeasuredDimension(width, height);
+ }
+
+ private int chooseWidth(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedWidth();
+ }
+ }
+
+ private int chooseHeight(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPreferedHeight();
+ }
+ }
+
+ private int getPrefferedWidth() {
+
+ int width = getPreferedHeight();
+
+ if (mShowAlphaPanel) {
+ width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT);
+ }
+
+
+ return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING);
+
+ }
+
+ private int getPreferedHeight() {
+
+ int height = (int)(200 * mDensity);
+
+ if (mShowAlphaPanel) {
+ height += PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ return height;
+ }
+
+
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = mDrawingOffset + getPaddingLeft();
+ mDrawingRect.right = w - mDrawingOffset - getPaddingRight();
+ mDrawingRect.top = mDrawingOffset + getPaddingTop();
+ mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom();
+
+ setUpSatValRect();
+ setUpHueRect();
+ setUpAlphaRect();
+ }
+
+ private void setUpSatValRect() {
+
+ final RectF dRect = mDrawingRect;
+ float panelSide = dRect.height() - BORDER_WIDTH_PX * 2;
+
+ if (mShowAlphaPanel) {
+ panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = top + panelSide;
+ float right = left + panelSide;
+
+ mSatValRect = new RectF(left,top, right, bottom);
+ }
+
+ private void setUpHueRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0);
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mHueRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpAlphaRect() {
+
+ if (!mShowAlphaPanel) return;
+
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mAlphaRect = new RectF(left, top, right, bottom);
+
+
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+ mAlphaPattern.setBounds(Math.round(mAlphaRect.left), Math
+ .round(mAlphaRect.top), Math.round(mAlphaRect.right), Math
+ .round(mAlphaRect.bottom));
+
+
+
+ }
+
+
+ /**
+ * Set a OnColorChangedListener to get notified when the color
+ * selected by the user has changed.
+ * @param listener
+ */
+ public void setOnColorChangedListener(OnColorChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Set the color of the border surrounding all panels.
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding all panels.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+ /**
+ * Get the current color this view is showing.
+ * @return the current color.
+ */
+ public int getColor() {
+ return Color.HSVToColor(mAlpha, new float[]{mHue,mSat,mVal});
+ }
+
+ /**
+ * Set the color the view should show.
+ * @param color The color that should be selected.
+ */
+ public void setColor(int color) {
+ setColor(color, false);
+ }
+
+ /**
+ * Set the color this view should show.
+ * @param color The color that should be selected.
+ * @param callback If you want to get a callback to
+ * your OnColorChangedListener.
+ */
+ public void setColor(int color, boolean callback) {
+
+ int alpha = Color.alpha(color);
+ int red = Color.red(color);
+ int blue = Color.blue(color);
+ int green = Color.green(color);
+
+ float[] hsv = new float[3];
+
+ Color.RGBToHSV(red, green, blue, hsv);
+
+ mAlpha = alpha;
+ mHue = hsv[0];
+ mSat = hsv[1];
+ mVal = hsv[2];
+
+ if (callback && mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[]{mHue, mSat, mVal}));
+ }
+
+ invalidate();
+ }
+
+ /**
+ * Get the drawing offset of the color picker view.
+ * The drawing offset is the distance from the side of
+ * a panel to the side of the view minus the padding.
+ * Useful if you want to have your own panel below showing
+ * the currently selected color and want to align it perfectly.
+ * @return The offset in pixels.
+ */
+ public float getDrawingOffset() {
+ return mDrawingOffset;
+ }
+
+ /**
+ * Set if the user is allowed to adjust the alpha panel. Default is false.
+ * If it is set to false no alpha will be set.
+ * @param visible
+ */
+ public void setAlphaSliderVisible(boolean visible) {
+
+ if (mShowAlphaPanel != visible) {
+ mShowAlphaPanel = visible;
+
+ /*
+ * Reset all shader to force a recreation.
+ * Otherwise they will not look right after
+ * the size of the view has changed.
+ */
+ mValShader = null;
+ mSatShader = null;
+ mHueShader = null;
+ mAlphaShader = null;;
+
+ requestLayout();
+ }
+
+ }
+
+ public void setSliderTrackerColor(int color) {
+ mSliderTrackerColor = color;
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+
+ invalidate();
+ }
+
+ public int getSliderTrackerColor() {
+ return mSliderTrackerColor;
+ }
+
+ /**
+ * Set the text that should be shown in the
+ * alpha slider. Set to null to disable text.
+ * @param res string resource id.
+ */
+ public void setAlphaSliderText(int res) {
+ String text = getContext().getString(res);
+ setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the
+ * alpha slider. Set to null to disable text.
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ mAlphaSliderText = text;
+ invalidate();
+ }
+
+ /**
+ * Get the current value of the text
+ * that will be shown in the alpha
+ * slider.
+ * @return
+ */
+ public String getAlphaSliderText() {
+ return mAlphaSliderText;
+ }
+
+ /**
+ * Method that checks the support for HardwareAcceleration. Check AOSP notice
+ *
+ *
+ * 'ComposeShader can only contain shaders of different types (a BitmapShader and a
+ * LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
+ * application is affected by any of these missing features or limitations, you can turn
+ * off hardware acceleration for just the affected portion of your application by calling
+ * setLayerType(View.LAYER_TYPE_SOFTWARE, null).'
+ */
+ private void checkHardwareAccelerationSupport() {
+ // HardwareAcceleration sit is only available since ICS. 14 = ICS_VERSION_CODE
+ if (android.os.Build.VERSION.SDK_INT >= 14) {
+ try{
+ // We need to use reflection to get that method to avoid compilation errors
+ Method isHardwareAccelerated =
+ getClass().getMethod("isHardwareAccelerated", new Class[]{});
+ Object o = isHardwareAccelerated.invoke(this, new Object[]{});
+ if (null != o && o instanceof Boolean && (Boolean)o) {
+ // HardwareAcceleration is supported. Use SoftwareAcceleration
+ Method setLayerType =
+ getClass().getMethod(
+ "setLayerType", int.class, Paint.class);
+ setLayerType.invoke(this, 1, (Paint)null);
+ }
+ } catch (Exception e) { /** NON BLOCK **/}
+ }
+ }
+
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/HighlightColors.java b/Backbone/src/main/java/com/ash/syntaxhighlight/HighlightColors.java
new file mode 100644
index 000000000..3bd18a9aa
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/HighlightColors.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight;
+
+import android.graphics.Color;
+
+/**
+ * An enumeration of all the color resources available for syntax highlight processors.
+ */
+public enum HighlightColors {
+
+ /**
+ * Text color
+ */
+ TEXT(
+ "ash_text", //$NON-NLS-1$
+ "ash_text_color", //$NON-NLS-1$
+ Color.argb(153, 0, 0, 0)),
+ /**
+ * Assignment text color
+ */
+ ASSIGNMENT(
+ "ash_assignment", //$NON-NLS-1$
+ "ash_assignment_color", //$NON-NLS-1$
+ Color.argb(153, 0, 0, 0)),
+ /**
+ * Single line comment color
+ */
+ SINGLE_LINE_COMMENT(
+ "ash_singleline_comment", //$NON-NLS-1$
+ "ash_singleline_comment_color", //$NON-NLS-1$
+ Color.argb(255, 63, 127, 95)),
+ /**
+ * Multiline line comment color
+ */
+ MULTILINE_LINE_COMMENT(
+ "ash_multiline_comment", //$NON-NLS-1$
+ "ash_multiline_comment_color", //$NON-NLS-1$
+ Color.argb(255, 127, 159, 191)),
+ /**
+ * Keyword color
+ */
+ KEYWORD(
+ "ash_keyword", //$NON-NLS-1$
+ "ash_keyword_color", //$NON-NLS-1$
+ Color.argb(255, 127, 0, 85)),
+ /**
+ * Quoted string color
+ */
+ QUOTED_STRING(
+ "ash_quoted_string", //$NON-NLS-1$
+ "ash_quoted_string_color", //$NON-NLS-1$
+ Color.argb(255, 42, 0, 255)),
+ /**
+ * Variable color
+ */
+ VARIABLE(
+ "ash_variable", //$NON-NLS-1$
+ "ash_variable_color", //$NON-NLS-1$
+ Color.argb(153, 0, 0, 192));
+
+
+ private final String mId;
+ private final String mResId;
+ private final int mDefault;
+
+ /**
+ * Constructor of HighlightColors
+ *
+ * @param id The id of the object
+ * @param resid The resource id
+ * @param def The default value
+ */
+ HighlightColors(String id, String resid, int def) {
+ this.mId = id;
+ this.mResId = resid;
+ this.mDefault = def;
+ }
+
+ /**
+ * Returns the identifier
+ *
+ * @return String The identifier
+ */
+ public String getId() {
+ return this.mId;
+ }
+
+ /**
+ * Returns the resource identifier
+ *
+ * @return String The resource identifier
+ */
+ public String getResId() {
+ return this.mResId;
+ }
+
+ /**
+ * Returns the default value
+ *
+ * @return String The default value
+ */
+ public int getDefault() {
+ return this.mDefault;
+ }
+
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/ISyntaxHighlightResourcesResolver.java b/Backbone/src/main/java/com/ash/syntaxhighlight/ISyntaxHighlightResourcesResolver.java
new file mode 100644
index 000000000..6494f573b
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/ISyntaxHighlightResourcesResolver.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight;
+
+/**
+ * An interface that should be implemented by the library caller, to
+ * resolve resources needed by the syntax processors.
+ *
+ * @see HighlightColors
+ */
+public interface ISyntaxHighlightResourcesResolver {
+
+ /**
+ * Method that returns a string
+ *
+ * @param id The color unique id
+ * @param resid The resource identifier
+ * @return CharSequence The string
+ */
+ CharSequence getString(String id, String resid);
+
+ /**
+ * Method that returns an integer
+ *
+ * @param id The color unique id
+ * @param resid The resource identifier
+ * @param def The default value
+ * @return int The integer value
+ */
+ int getInteger(String id, String resid, int def);
+
+ /**
+ * Method that returns a color
+ *
+ * @param id The color unique id
+ * @param resid The resource identifier
+ * @param def The default value
+ * @return int The color
+ */
+ int getColor(String id, String resid, int def);
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/RegExpUtil.java b/Backbone/src/main/java/com/ash/syntaxhighlight/RegExpUtil.java
new file mode 100644
index 000000000..01c7f507a
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/RegExpUtil.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * A helper class for deal with patters
+ */
+public final class RegExpUtil {
+
+ /**
+ * A constant that is returned when the no expression matches.
+ */
+ public static final int NO_MATCH = -1;
+
+ /**
+ * New line pattern
+ */
+ public static final Pattern NEWLINE_PATTERN = Pattern.compile("(\r\n|\n|\r)"); //$NON-NLS-1$
+
+ /**
+ * Method that returns the last match position of a regexp,
+ *
+ * @param pattern The patter
+ * @param input The input
+ * @param withPattern Whether the return position should contains the pattern or not.
+ * @return int The matched position or -1
+ */
+ public static int getLastMatch(Pattern pattern, CharSequence input, boolean withPattern) {
+ Matcher m = pattern.matcher(input);
+ int p = NO_MATCH;
+ while (m.find()) {
+ p = withPattern ? m.start() : m.end();
+ }
+ return p;
+ }
+
+ /**
+ * Method that returns the next match position of a regexp,
+ *
+ * @param pattern The patter
+ * @param input The input
+ * @param withPattern Whether the return position should contains the pattern or not.
+ * @return int The matched position or -1
+ */
+ public static int getNextMatch(Pattern pattern, CharSequence input, boolean withPattern) {
+ Matcher m = pattern.matcher(input);
+ int p = NO_MATCH;
+ if (m.find()) {
+ return withPattern ? m.end() : m.start();
+ }
+ return p;
+ }
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightFactory.java b/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightFactory.java
new file mode 100644
index 000000000..c4231b0f1
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightFactory.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight;
+
+import com.ash.syntaxhighlight.spi.PropertiesSyntaxHighlightProcessor;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A factory of SyntaxHighlightProcessor classes.
+ */
+public class SyntaxHighlightFactory {
+
+ private static SyntaxHighlightFactory sFactory;
+
+ private final ArrayList mProcessors;
+
+ /**
+ * Constructor of SyntaxHighlightFactory
+ */
+ public SyntaxHighlightFactory() {
+ super();
+ this.mProcessors = new ArrayList();
+ }
+
+ /**
+ * Method that returns the default highlight factory instance
+ *
+ * @param resolver A class for allow the processor to obtain resources
+ * @return SyntaxHighlightFactory The default syntax highlight factory
+ */
+ public static final synchronized SyntaxHighlightFactory getDefaultFactory(
+ ISyntaxHighlightResourcesResolver resolver) {
+ if (sFactory == null) {
+ sFactory = createDefaultFactory(resolver);
+ }
+ return sFactory;
+ }
+
+ /**
+ * Method that returns the syntax highlight processor that can handle the file
+ *
+ * @param file The file to process
+ * @return SyntaxHighlightProcessor The syntax highlight processor
+ */
+ public SyntaxHighlightProcessor getSyntaxHighlightProcessor(File file) {
+ int cc = this.mProcessors.size();
+ for (int i = 0; i < cc; i++) {
+ SyntaxHighlightProcessor processor = this.mProcessors.get(i);
+ if (processor.accept(file)) {
+ return processor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Method that return all the available syntax highlight processors.
+ *
+ * @return List the list available syntax highlight processors.
+ */
+ public List getAvailableSyntaxHighlightProcessors() {
+ return new ArrayList(this.mProcessors);
+ }
+
+ /**
+ * Method that create the default syntax highlight factory.
+ *
+ * @param resolver A class for allow the processor to obtain resources
+ * @return SyntaxHighlightFactory The default factory
+ */
+ private static SyntaxHighlightFactory createDefaultFactory(
+ ISyntaxHighlightResourcesResolver resolver) {
+ // TODO Read all processors classes of the SPI package
+ // For now we add all known syntax highlight processors
+ SyntaxHighlightFactory factory = new SyntaxHighlightFactory();
+ factory.mProcessors.add(new PropertiesSyntaxHighlightProcessor(resolver));
+ return factory;
+ }
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightProcessor.java b/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightProcessor.java
new file mode 100644
index 000000000..a57485d76
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/SyntaxHighlightProcessor.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight;
+
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+
+import java.io.File;
+
+/**
+ * The base class for all the syntax highlight processors.
+ */
+public abstract class SyntaxHighlightProcessor {
+
+ protected final ISyntaxHighlightResourcesResolver mResourcesResolver;
+
+ /**
+ * Constructor of SyntaxHighlightProcessor
+ *
+ * @param resolver A class for resolve resources
+ */
+ public SyntaxHighlightProcessor(ISyntaxHighlightResourcesResolver resolver) {
+ super();
+ this.mResourcesResolver = resolver;
+ }
+
+ /**
+ * Method that request to the syntax highlight processor if it is able to parse
+ * the file
+ *
+ * @param file The file to check
+ * @return boolean If the syntax highlight processor accepts process the file
+ */
+ protected abstract boolean accept(File file);
+
+ /**
+ * Method that initializes the processor
+ */
+ public abstract void initialize();
+
+ /**
+ * Method that request to the syntax highlight processor to do process and highlight a
+ * document. This method request a full process.
+ *
+ * @param spanable The spannable source to highlight
+ */
+ public abstract void process(Spannable spanable);
+
+ /**
+ * Method that request to the syntax highlight processor to process and highlight a
+ * document. This method request a partial process.
+ *
+ * @param spanable The spannable source to highlight
+ * @param start The start of spannable to process
+ * @param end The end of spannable to process
+ */
+ public abstract void process(Spannable spanable, int start, int end);
+
+ /**
+ * Method that cancels the active processor
+ */
+ public abstract void cancel();
+
+ /**
+ * Method that clear all the existent spans
+ *
+ * @param spanable The spannable
+ */
+ @SuppressWarnings("static-method")
+ public void clear(Spannable spanable) {
+ ForegroundColorSpan[] spans =
+ spanable.getSpans(0, spanable.length(), ForegroundColorSpan.class);
+ int cc = spans.length;
+ for (int i = 0; i < cc; i++) {
+ spanable.removeSpan(spans[i]);
+ }
+ }
+
+
+ /**
+ * Method that sets a new Spannable.
+ *
+ * @param spanable The spannable
+ * @param color The color of the span
+ * @param start The start of the span
+ * @param end The end of the span
+ */
+ @SuppressWarnings("static-method")
+ protected void setSpan(Spannable spanable, int color, int start, int end) {
+ if (start == end) return;
+ spanable.setSpan(
+ new ForegroundColorSpan(color),
+ start,
+ end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/NewLineScanner.java b/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/NewLineScanner.java
new file mode 100644
index 000000000..7ae262bcf
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/NewLineScanner.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight.scanners;
+
+import com.ash.syntaxhighlight.RegExpUtil;
+
+import java.util.regex.Matcher;
+
+/**
+ * An scanner to process an input, reporting every text into new lines.
+ */
+public class NewLineScanner extends Scanner {
+
+ private final NewLineScannerListener mListener;
+
+ /**
+ * The listener for the newline scanner
+ */
+ public interface NewLineScannerListener {
+ /**
+ * When a new line is ready
+ *
+ * @param newline The newline detected
+ * @param start The start position of the new line within the input text
+ * @param end The end position of the new line within the input text
+ * @param sep The line separator detected
+ * @return boolean If processor must continue with the next line
+ */
+ boolean onNewLine(CharSequence newline, int start, int end, CharSequence sep);
+ }
+
+ /**
+ * Constructor of Scanner
+ *
+ * @param input The input
+ * @param listener The listener where return every new line
+ */
+ public NewLineScanner(CharSequence input, NewLineScannerListener listener) {
+ super(input);
+ this.mListener = listener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void scan() {
+ if (this.mInput.length() == 0) return;
+ Matcher m = RegExpUtil.NEWLINE_PATTERN.matcher(this.mInput);
+ int next = 0;
+ while(m.find(next)) {
+ CharSequence line = this.mInput.subSequence(next, m.start());
+ if (!this.mListener.onNewLine(line, next, m.start(), m.group())) {
+ return;
+ }
+ next = m.end();
+ }
+ // The non-matched data
+ CharSequence line = this.mInput.subSequence(next, this.mInput.length());
+ this.mListener.onNewLine(line, next, this.mInput.length(), null);
+ }
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/Scanner.java b/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/Scanner.java
new file mode 100644
index 000000000..1da5f1b77
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/scanners/Scanner.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight.scanners;
+
+
+/**
+ * The base class for all the scanners
+ */
+public abstract class Scanner {
+
+ CharSequence mInput;
+
+ /**
+ * Constructor of Scanner
+ *
+ * @param input The input
+ */
+ public Scanner(CharSequence input) {
+ super();
+ this.mInput = input;
+ }
+
+ /**
+ * Method that starts the scan process
+ */
+ public abstract void scan();
+}
diff --git a/Backbone/src/main/java/com/ash/syntaxhighlight/spi/PropertiesSyntaxHighlightProcessor.java b/Backbone/src/main/java/com/ash/syntaxhighlight/spi/PropertiesSyntaxHighlightProcessor.java
new file mode 100644
index 000000000..80d52db04
--- /dev/null
+++ b/Backbone/src/main/java/com/ash/syntaxhighlight/spi/PropertiesSyntaxHighlightProcessor.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2013 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.ash.syntaxhighlight.spi;
+
+import android.text.Spannable;
+import android.text.style.ForegroundColorSpan;
+import com.ash.syntaxhighlight.HighlightColors;
+import com.ash.syntaxhighlight.ISyntaxHighlightResourcesResolver;
+import com.ash.syntaxhighlight.RegExpUtil;
+import com.ash.syntaxhighlight.SyntaxHighlightProcessor;
+import com.ash.syntaxhighlight.scanners.NewLineScanner;
+import com.ash.syntaxhighlight.scanners.NewLineScanner.NewLineScannerListener;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A properties highlight processor class.
+ *
+ * The behaviour of this class is:
+ *
+ * - Comments start with # (only spaces are allowed prior to comment)
+ * - Assignment character (=) separates key from value
+ * - Arguments exists only in values, and are composed by {a digit}
+ * - Values can be extended in multiple lines if line ends with the char "\". A
+ * comment in multiline breaks the multiline and starts a new property.
+ *
+ *
+ * IMP! This class is not thread safe. Calling "process" methods should be
+ * done in a synchronous way.
+ */
+public class PropertiesSyntaxHighlightProcessor extends SyntaxHighlightProcessor {
+
+ private static final String EXT_PROP = "prop"; //$NON-NLS-1$
+ private static final String EXT_PROPERTIES = "properties"; //$NON-NLS-1$
+
+ private static final Pattern COMMENT = Pattern.compile("^\\s*#.*"); //$NON-NLS-1$
+ private static final Pattern MULTILINE = Pattern.compile(".*\\\\\\s*$"); //$NON-NLS-1$
+ private static final Pattern ASSIGNMENT = Pattern.compile("="); //$NON-NLS-1$
+ private static final Pattern ARGUMENT = Pattern.compile("\\{\\d+\\}"); //$NON-NLS-1$
+
+ protected Spannable mSpannable;
+ private boolean mMultiLine;
+
+ private int mKeyColor;
+ private int mAssignmentColor;
+ private int mCommentColor;
+ private int mValueColor;
+ private int mArgumentColor;
+
+ /**
+ * Constructor of PropertiesSyntaxHighlightProcessor
+ *
+ * @param resolver A class for resolve resources
+ */
+ public PropertiesSyntaxHighlightProcessor(ISyntaxHighlightResourcesResolver resolver) {
+ super(resolver);
+ initialize();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean accept(File file) {
+ if (file == null) return false;
+ return file.getName().toLowerCase().endsWith(EXT_PROP) ||
+ file.getName().toLowerCase().endsWith(EXT_PROPERTIES);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initialize() {
+ this.mMultiLine = false;
+ this.mSpannable = null;
+ if (this.mResourcesResolver != null) {
+ this.mKeyColor = this.mResourcesResolver.getColor(
+ HighlightColors.TEXT.getId(),
+ HighlightColors.TEXT.getResId(),
+ HighlightColors.TEXT.getDefault());
+ this.mAssignmentColor = this.mResourcesResolver.getColor(
+ HighlightColors.ASSIGNMENT.getId(),
+ HighlightColors.ASSIGNMENT.getResId(),
+ HighlightColors.ASSIGNMENT.getDefault());
+ this.mCommentColor = this.mResourcesResolver.getColor(
+ HighlightColors.SINGLE_LINE_COMMENT.getId(),
+ HighlightColors.SINGLE_LINE_COMMENT.getResId(),
+ HighlightColors.SINGLE_LINE_COMMENT.getDefault());
+ this.mValueColor = this.mResourcesResolver.getColor(
+ HighlightColors.VARIABLE.getId(),
+ HighlightColors.VARIABLE.getResId(),
+ HighlightColors.VARIABLE.getDefault());
+ this.mArgumentColor = this.mResourcesResolver.getColor(
+ HighlightColors.KEYWORD.getId(),
+ HighlightColors.KEYWORD.getResId(),
+ HighlightColors.KEYWORD.getDefault());
+ } else {
+ // By default
+ this.mKeyColor = HighlightColors.TEXT.getDefault();
+ this.mAssignmentColor = HighlightColors.TEXT.getDefault();
+ this.mCommentColor = HighlightColors.SINGLE_LINE_COMMENT.getDefault();
+ this.mValueColor = HighlightColors.VARIABLE.getDefault();
+ this.mArgumentColor = HighlightColors.KEYWORD.getDefault();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void process(final Spannable spanable) {
+ this.mMultiLine = false;
+ this.mSpannable = spanable;
+ clear(spanable);
+ NewLineScanner scanner = new NewLineScanner(spanable, new NewLineScannerListener() {
+ @Override
+ public boolean onNewLine(CharSequence newline, int start, int end, CharSequence sep) {
+ processNewLine(newline, start, end);
+ return true;
+ }
+
+ });
+ scanner.scan();
+ this.mSpannable = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void process(final Spannable spanable, final int start, final int end) {
+ // We need a Retrieve the previous line
+ this.mMultiLine = false;
+ this.mSpannable = spanable;
+ CharSequence seqs = spanable.subSequence(0, start);
+ CharSequence seqe = spanable.subSequence(end, spanable.length());
+ int s1 = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqs, false);
+ if (s1 == RegExpUtil.NO_MATCH) {
+ s1 = 0;
+ }
+ int e1 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, false);
+ if (e1 == RegExpUtil.NO_MATCH) {
+ e1 = spanable.length();
+ } else {
+ e1 += end;
+ }
+
+ // Also, we need to know about if the previous line is multiline
+ if (s1 > 0) {
+ int s2 = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqs, true);
+ CharSequence seqnl = spanable.subSequence(0, s2);
+ int snl = RegExpUtil.getLastMatch(RegExpUtil.NEWLINE_PATTERN, seqnl, false);
+ Matcher mlm = MULTILINE.matcher(
+ spanable.subSequence(snl != RegExpUtil.NO_MATCH ? snl : 0, s2));
+ this.mMultiLine = mlm.matches();
+ }
+
+ // Process the new line
+ if (s1 != e1) {
+ processNewLine(spanable.subSequence(s1, e1), s1, e1);
+ }
+
+ // Now, multiline again (next line). We check always the next line, because we
+ // don't know if user delete multiline flag in the current line
+ e1 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, true);
+ if (e1 != RegExpUtil.NO_MATCH) {
+ e1 += end;
+ seqe = spanable.subSequence(e1, spanable.length());
+ int e2 = RegExpUtil.getNextMatch(RegExpUtil.NEWLINE_PATTERN, seqe, false);
+ if (e2 == RegExpUtil.NO_MATCH) {
+ e2 = spanable.length();
+ } else {
+ e2 += e1;
+ }
+ processNewLine(spanable.subSequence(e1, e2), e1, e2);
+ }
+
+ this.mSpannable = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void cancel() {
+ // Not needed by this processor
+ }
+
+ /**
+ * A method to process every new line
+ *
+ * @param newline The newline
+ * @param start The start position of the line
+ * @param end The end position of the line
+ * @hide
+ */
+ void processNewLine(CharSequence newline, int start, int end) {
+ // Remove all spannable of the line (this processor doesn't multiline spans and
+ // only uses ForegroundColorSpan spans)
+ ForegroundColorSpan[] spans =
+ this.mSpannable.getSpans(start, end, ForegroundColorSpan.class);
+ int cc = spans.length;
+ for (int i = 0; i < cc; i++) {
+ this.mSpannable.removeSpan(spans[i]);
+ }
+
+ // Find comment
+ Matcher cm = COMMENT.matcher(newline);
+ if (cm.matches()) {
+ // All the line is a comment
+ setSpan(this.mSpannable, this.mCommentColor, start, end);
+ this.mMultiLine = false;
+ return;
+ }
+
+ // Has multiline
+ Matcher mlm = MULTILINE.matcher(newline);
+ boolean ml = mlm.matches();
+
+ //Find the assignment
+ int k = this.mMultiLine ? -1 : start;
+ int v = start;
+ int v2 = 0;
+ int a = -1;
+ if (!this.mMultiLine) {
+ Matcher am = ASSIGNMENT.matcher(newline);
+ if (am.find()) {
+ // Assignment found
+ v2 = am.start() + 1;
+ a = start + am.start();
+ v = a + 1;
+ }
+ }
+
+ // All the string is a key
+ if (!this.mMultiLine && a == -1) {
+ setSpan(this.mSpannable, this.mKeyColor, start, end);
+
+ } else {
+ // Key
+ if (!this.mMultiLine) {
+ setSpan(this.mSpannable, this.mKeyColor, k, a);
+ }
+ // Assignment
+ if (!this.mMultiLine) {
+ setSpan(this.mSpannable, this.mAssignmentColor, a, a + 1);
+ }
+ // Value
+ setSpan(this.mSpannable, this.mValueColor, v, end);
+ // Argument
+ Matcher argm = ARGUMENT.matcher(newline);
+ while (argm.find(v2)) {
+ int s = start + argm.start();
+ int e = start + argm.end();
+ setSpan(this.mSpannable, this.mArgumentColor, s, e);
+ v2 = argm.end();
+ }
+ }
+
+ // Multiline?
+ this.mMultiLine = ml;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/FileManagerApplication.java b/Backbone/src/main/java/me/toolify/backbone/FileManagerApplication.java
similarity index 84%
rename from src/com/cyanogenmod/filemanager/FileManagerApplication.java
rename to Backbone/src/main/java/me/toolify/backbone/FileManagerApplication.java
index a25126fb3..38b1e7780 100644
--- a/src/com/cyanogenmod/filemanager/FileManagerApplication.java
+++ b/Backbone/src/main/java/me/toolify/backbone/FileManagerApplication.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager;
+package me.toolify.backbone;
import android.app.Application;
import android.content.BroadcastReceiver;
@@ -24,23 +24,24 @@
import android.content.pm.ApplicationInfo;
import android.util.Log;
-import com.cyanogenmod.filemanager.console.Console;
-import com.cyanogenmod.filemanager.console.ConsoleAllocException;
-import com.cyanogenmod.filemanager.console.ConsoleBuilder;
-import com.cyanogenmod.filemanager.console.ConsoleHolder;
-import com.cyanogenmod.filemanager.console.shell.PrivilegedConsole;
-import com.cyanogenmod.filemanager.preferences.AccessMode;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
-import com.cyanogenmod.filemanager.util.AIDHelper;
-import com.cyanogenmod.filemanager.util.FileHelper;
-import com.cyanogenmod.filemanager.util.MimeTypeHelper;
+import me.toolify.backbone.console.Console;
+import me.toolify.backbone.console.ConsoleAllocException;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.console.ConsoleHolder;
+import me.toolify.backbone.console.shell.PrivilegedConsole;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.ObjectStringIdentifier;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.util.AIDHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
import java.io.File;
import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
/**
@@ -55,11 +56,13 @@ public final class FileManagerApplication extends Application {
private static boolean DEBUG = false;
private static Properties sSystemProperties;
+ private static Map sOptionalCommandsMap;
+
/**
* A constant that contains the main process name.
* @hide
*/
- public static final String MAIN_PROCESS = "com.cyanogenmod.filemanager"; //$NON-NLS-1$
+ public static final String MAIN_PROCESS = "me.toolify.backbone"; //$NON-NLS-1$
//Static resources
private static FileManagerApplication sApp;
@@ -68,6 +71,8 @@ public final class FileManagerApplication extends Application {
private static boolean sIsDebuggable = false;
private static boolean sIsDeviceRooted = false;
+ public static int NUM_PAGES = 2;
+
private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -235,6 +240,9 @@ private void init() {
// Check if the device is rooted
sIsDeviceRooted = areShellCommandsPresent();
+ // Check optional commands
+ loadOptionalCommands();
+
//Sets the default preferences if no value is set yet
Preferences.loadDefaults();
@@ -301,6 +309,19 @@ public static boolean isDeviceRooted() {
return sIsDeviceRooted;
}
+ /**
+ * Method that returns if a command is present in the system
+ *
+ * @param commandId The command key
+ * @return boolean If the command is present
+ */
+ public static boolean hasOptionalCommand(String commandId) {
+ if (!sOptionalCommandsMap.containsKey(commandId)) {
+ return false;
+ }
+ return sOptionalCommandsMap.get(commandId).booleanValue();
+ }
+
/**
* Method that returns a system property value
*
@@ -354,13 +375,11 @@ private static synchronized void allocBackgroundConsole(Context ctx) {
if (ConsoleBuilder.isPrivileged()) {
sBackgroundConsole =
new ConsoleHolder(
- ConsoleBuilder.createPrivilegedConsole(
- ctx, FileHelper.ROOT_DIRECTORY));
+ ConsoleBuilder.createPrivilegedConsole(ctx));
} else {
sBackgroundConsole =
new ConsoleHolder(
- ConsoleBuilder.createNonPrivilegedConsole(
- ctx, FileHelper.ROOT_DIRECTORY));
+ ConsoleBuilder.createNonPrivilegedConsole(ctx));
}
} catch (Exception e) {
Log.e(TAG,
@@ -389,8 +408,7 @@ public static void changeBackgroundConsoleToPriviligedConsole()
sBackgroundConsole =
new ConsoleHolder(
ConsoleBuilder.createPrivilegedConsole(
- getInstance().getApplicationContext(),
- FileHelper.ROOT_DIRECTORY));
+ getInstance().getApplicationContext()));
} catch (Exception e) {
try {
if (sBackgroundConsole != null) {
@@ -410,6 +428,9 @@ public static void changeBackgroundConsoleToPriviligedConsole()
* @return boolean If the access mode of the application
*/
public static AccessMode getAccessMode() {
+ if (!sIsDeviceRooted) {
+ return AccessMode.SAFE;
+ }
String defaultValue =
((ObjectStringIdentifier)FileManagerSettings.
SETTINGS_ACCESS_MODE.getDefaultValue()).getId();
@@ -472,4 +493,37 @@ private boolean areShellCommandsPresent() {
}
return false;
}
+
+ @SuppressWarnings("boxing")
+ private void loadOptionalCommands() {
+ try {
+ sOptionalCommandsMap = new HashMap();
+
+ String shellCommands = getString(R.string.shell_optional_commands);
+ String[] commands = shellCommands.split(","); //$NON-NLS-1$
+ int cc = commands.length;
+ if (cc == 0) {
+ Log.w(TAG, "No optional commands."); //$NON-NLS-1$
+ return;
+ }
+ for (int i = 0; i < cc; i++) {
+ String c = commands[i].trim();
+ String key = c.substring(0, c.indexOf("=")).trim(); //$NON-NLS-1$
+ c = c.substring(c.indexOf("=")+1).trim(); //$NON-NLS-1$
+ if (c.length() == 0) continue;
+ File cmd = new File(c);
+ Boolean found = Boolean.valueOf(cmd.exists() && cmd.isFile());
+ sOptionalCommandsMap.put(key, found);
+ if (DEBUG) {
+ Log.w(TAG,
+ String.format(
+ "Optional command %s %s.", //$NON-NLS-1$
+ c, found ? "found" : "not found")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG,
+ "Failed to read optional shell commands.", e); //$NON-NLS-1$
+ }
+ }
}
diff --git a/Backbone/src/main/java/me/toolify/backbone/actionmode/PropertiesModeCallback.java b/Backbone/src/main/java/me/toolify/backbone/actionmode/PropertiesModeCallback.java
new file mode 100644
index 000000000..866a8e7ca
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/actionmode/PropertiesModeCallback.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.actionmode;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.view.ActionMode;
+import android.view.InflateException;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.ShareActionProvider;
+
+import java.util.List;
+
+import de.greenrobot.event.EventBus;
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.bus.events.BookmarkRefreshEvent;
+import me.toolify.backbone.bus.events.ClosePropertiesDrawerEvent;
+import me.toolify.backbone.listeners.OnCopyMoveListener;
+import me.toolify.backbone.listeners.OnRequestRefreshListener;
+import me.toolify.backbone.listeners.OnSelectionListener;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.ui.dialogs.InputNameDialog;
+import me.toolify.backbone.ui.policy.BookmarksActionPolicy;
+import me.toolify.backbone.ui.policy.CompressActionPolicy;
+import me.toolify.backbone.ui.policy.CopyMoveActionPolicy;
+import me.toolify.backbone.ui.policy.DeleteActionPolicy;
+import me.toolify.backbone.ui.policy.ExecutionActionPolicy;
+import me.toolify.backbone.ui.policy.InfoActionPolicy;
+import me.toolify.backbone.ui.policy.IntentsActionPolicy;
+import me.toolify.backbone.ui.policy.NavigationActionPolicy;
+import me.toolify.backbone.ui.policy.NewActionPolicy;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.StorageHelper;
+
+public class PropertiesModeCallback implements ActionMode.Callback {
+
+ private MenuItem mActionCreateLinkGlobal;
+ private MenuItem mActionOpen;
+ private MenuItem mActionOpenWith;
+ private MenuItem mActionDelete;
+ private MenuItem mActionRename;
+ private MenuItem mActionCompress;
+ private MenuItem mActionExtract;
+ private MenuItem mActionCreateLink;
+ private MenuItem mActionExecute;
+ private MenuItem mActionSend;
+ private MenuItem mActionAddBookmark;
+ private MenuItem mActionAddShortcut;
+ private MenuItem mActionChecksum;
+ private ShareActionProvider mShareActionProvider;
+
+ private boolean pasteReady = false;
+
+ boolean mClosedByUser = true;
+ private Activity mActivity;
+ private ActionMode mPropertiesMode;
+ private Boolean mGlobal;
+ private final Boolean mChRooted;
+
+ /**
+ * @hide
+ */
+ OnRequestRefreshListener mOnRequestRefreshListener;
+ /**
+ * @hide
+ */
+ OnSelectionListener mOnSelectionListener;
+ /**
+ * @hide
+ */
+ OnCopyMoveListener onCopyMoveListener;
+
+ private FileSystemObject mFso;
+
+ /**
+ * Constructor for SelectionModeCallback.
+ *
+ * @param activity The current Activity context
+ * @param fso The FileSystemObject to present options for
+ */
+ public PropertiesModeCallback(Activity activity, FileSystemObject fso) {
+ this.mActivity = activity;
+ this.mFso = fso;
+ this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
+ }
+
+ /**
+ * Method that sets the listener for communicate a refresh request.
+ *
+ * @param onRequestRefreshListener The request refresh listener
+ */
+ public void setOnRequestRefreshListener(OnRequestRefreshListener onRequestRefreshListener) {
+ this.mOnRequestRefreshListener = onRequestRefreshListener;
+ }
+
+ /**
+ * Method that sets the listener for requesting selection data
+ *
+ * @param onSelectionListener The request selection data listener
+ */
+ public void setOnSelectionListener(OnSelectionListener onSelectionListener) {
+ this.mOnSelectionListener = onSelectionListener;
+ }
+
+ /**
+ * Method that sets the listener for marking file selections for paste
+ *
+ * @param onCopyMoveListener The request selection data listener
+ */
+ public void setOnCopyMoveListener(OnCopyMoveListener onCopyMoveListener) {
+ this.onCopyMoveListener = onCopyMoveListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mPropertiesMode = mode;
+
+ MenuInflater inflater = mActivity.getMenuInflater();
+ inflater.inflate(R.menu.properties_actionmode, menu);
+
+ mActionCreateLinkGlobal = menu.findItem(R.id.mnu_actions_create_link_global);
+ mActionOpen = menu.findItem(R.id.mnu_actions_open);
+ mActionOpenWith = menu.findItem(R.id.mnu_actions_open_with);
+ mActionDelete = menu.findItem(R.id.mnu_actions_delete);
+ mActionRename = menu.findItem(R.id.mnu_actions_rename);
+ mActionCompress = menu.findItem(R.id.mnu_actions_compress);
+ mActionExtract = menu.findItem(R.id.mnu_actions_extract);
+ mActionCreateLink = menu.findItem(R.id.mnu_actions_create_link);
+ mActionExecute = menu.findItem(R.id.mnu_actions_execute);
+ mActionSend = menu.findItem(R.id.mnu_actions_send);
+ mActionAddBookmark = menu.findItem(R.id.mnu_actions_add_to_bookmarks);
+ mActionAddShortcut = menu.findItem(R.id.mnu_actions_add_shortcut);
+ mActionChecksum = menu.findItem(R.id.mnu_actions_compute_checksum);
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+
+ //TODO: Figure out how to change/fix the mActionCreateLinkGlobal flag
+ this.mGlobal = false;
+
+ // Reset action item visibility
+ mActionCreateLinkGlobal.setVisible(true);
+ mActionOpen.setVisible(true);
+ mActionOpenWith.setVisible(true);
+ mActionDelete.setVisible(true);
+ mActionRename.setVisible(true);
+ mActionCompress.setVisible(true);
+ mActionExtract.setVisible(true);
+ mActionCreateLink.setVisible(true);
+ mActionExecute.setVisible(true);
+ mActionSend.setVisible(true);
+ mActionAddBookmark.setVisible(true);
+ mActionAddShortcut.setVisible(true);
+ mActionChecksum.setVisible(true);
+
+ /*
+ * Single file mode
+ *
+ * Remove the following actions if we are dealing with a single file
+ */
+
+ // Check actions that needs a valid reference
+ if (this.mFso != null) {
+
+ // Hide Send/Open/Open With actions if the fso is folder or a system file
+ if (FileHelper.isDirectory(this.mFso) || FileHelper.isSystemFile(this.mFso)) {
+ mActionOpen.setVisible(false);
+ mActionOpenWith.setVisible(false);
+ mActionSend.setVisible(false);
+ }
+
+ // Create link (not allow in storage volume)
+ if (StorageHelper.isPathInStorageVolume(this.mFso.getFullPath())) {
+ mActionCreateLink.setVisible(false);
+ }
+
+ //Hide Execute action unless the mime/type category is EXEC
+ MimeTypeHelper.MimeTypeCategory category = MimeTypeHelper.getCategory(this.mActivity, this.mFso);
+ if (category.compareTo(MimeTypeHelper.MimeTypeCategory.EXEC) != 0) {
+ mActionExecute.setVisible(false);
+ }
+ }
+
+ // Hide "Add Bookmark" if the fso is the root directory (Already has a bookmark!)
+ if (this.mFso != null && FileHelper.isRootDirectory(this.mFso)) {
+ mActionAddBookmark.setVisible(false);
+ }
+
+ /*
+ * Multi-file mode
+ *
+ * Remove the following actions if we are dealing with a multi-fso selection
+ */
+
+ if (!this.mGlobal) {
+ // Create link (not allow in storage volume)
+ if (StorageHelper.isPathInStorageVolume(this.mFso.getFullPath())) {
+ mActionCreateLink.setVisible(false);
+ }
+ }
+
+ // Hide extract (uncompress/unzip) action for non-supported files
+ if (!FileHelper.isSupportedUncompressedFile(this.mFso)) {
+ mActionExtract.setVisible(false);
+ }
+
+ // Hide actions that can't be present when running in unprivileged mode)
+ if (this.mChRooted) {
+ mActionCreateLink.setVisible(false);
+ mActionCreateLinkGlobal.setVisible(false);
+ mActionExecute.setVisible(false);
+
+ // NOTE: This actions are not implemented in chrooted environments. The reason is
+ // that the main target of this application is CyanogenMod (a rooted environment).
+ // Adding this actions requires the use of commons-compress, an external Apache
+ // library that will add more size to the ending apk.
+ // For now, will maintain without implementation. Maybe, in the future.
+ mActionCompress.setVisible(false);
+ mActionExtract.setVisible(false);
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
+
+ switch (menuItem.getItemId()) {
+
+ //- Rename
+ case R.id.mnu_actions_rename:
+ showFsoInputNameDialog(menuItem, this.mFso, false);
+ finish();
+ return true;
+
+ //- Create link
+ case R.id.mnu_actions_create_link:
+ showFsoInputNameDialog(menuItem, this.mFso, true);
+ finish();
+ return true;
+
+ case R.id.mnu_actions_create_link_global:
+ showFsoInputNameDialog(menuItem, this.mFso, true);
+ finish();
+ return true;
+
+ //- Delete
+ case R.id.mnu_actions_delete:
+ DeleteActionPolicy.removeFileSystemObject(
+ this.mActivity,
+ this.mFso,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener,
+ null);
+ finish();
+ break;
+
+ //- Open
+ case R.id.mnu_actions_open:
+ IntentsActionPolicy.openFileSystemObject(
+ this.mActivity, this.mFso, false, null, null);
+ finish();
+ break;
+ //- Open with
+ case R.id.mnu_actions_open_with:
+ IntentsActionPolicy.openFileSystemObject(
+ this.mActivity, this.mFso, true, null, null);
+ finish();
+ break;
+
+ //- Execute
+ case R.id.mnu_actions_execute:
+ ExecutionActionPolicy.execute(this.mActivity, this.mFso);
+ finish();
+ break;
+
+ //- Send
+ case R.id.mnu_actions_send:
+ IntentsActionPolicy.sendFileSystemObject(
+ this.mActivity, this.mFso, null, null);
+ finish();
+ break;
+
+ //- Create copy
+ case R.id.mnu_actions_copy:
+ // Create a copy of the fso
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ CopyMoveActionPolicy.createCopyFileSystemObject(
+ selection,
+ this.onCopyMoveListener);
+ finish();
+ }
+ break;
+
+ // Move selection
+ case R.id.mnu_actions_move:
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ CopyMoveActionPolicy.createMoveFileSystemObject(
+ selection,
+ this.onCopyMoveListener);
+ finish();
+ }
+ break;
+
+ //- Uncompress
+ case R.id.mnu_actions_extract:
+ CompressActionPolicy.uncompress(
+ this.mActivity,
+ this.mFso,
+ this.mOnRequestRefreshListener);
+ break;
+ //- Checksum
+ case R.id.mnu_actions_compute_checksum:
+ InfoActionPolicy.showComputeChecksumDialog(
+ this.mActivity,
+ this.mFso);
+ break;
+ //- Compress
+ case R.id.mnu_actions_compress:
+ if (this.mOnSelectionListener != null) {
+ CompressActionPolicy.compress(
+ this.mActivity,
+ this.mFso,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener);
+ finish();
+ }
+ break;
+
+ //- Add to bookmarks
+ case R.id.mnu_actions_add_to_bookmarks:
+ BookmarksActionPolicy.addToBookmarks(this.mActivity, this.mFso);
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ break;
+
+ //- Add shortcut
+ case R.id.mnu_actions_add_shortcut:
+ IntentsActionPolicy.createShortcut(this.mActivity, this.mFso);
+ break;
+
+ //- Navigate to parent
+ case R.id.mnu_actions_open_parent_folder:
+ NavigationActionPolicy.openParentFolder(
+ this.mActivity, this.mFso, this.mOnRequestRefreshListener);
+ break;
+
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mPropertiesMode = null;
+ EventBus.getDefault().post(new ClosePropertiesDrawerEvent());
+ }
+
+ public void finish() {
+ mPropertiesMode.finish();
+ }
+
+ public void setClosedByUser(boolean closedByUser) {
+ this.mClosedByUser = closedByUser;
+ }
+
+ public boolean inPropertiesActionMode() {
+ return mPropertiesMode != null;
+ }
+
+ public void refresh() {
+ mPropertiesMode.invalidate();
+ }
+
+ /**
+ * Method that show a new dialog for input a name for an existing fso.
+ *
+ * @param menuItem The item menu associated
+ * @param fso The file system object
+ * @param allowFsoName If allow that the name of the fso will be returned
+ */
+ private void showFsoInputNameDialog(
+ final MenuItem menuItem, final FileSystemObject fso, final boolean allowFsoName) {
+
+ //Show the input name dialog
+ final InputNameDialog inputNameDialog =
+ new InputNameDialog(
+ this.mActivity,
+ this.mOnSelectionListener.onRequestCurrentItems(),
+ fso,
+ allowFsoName,
+ menuItem.getTitle().toString());
+ inputNameDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ //Retrieve the name an execute the action
+ try {
+ String name = inputNameDialog.getName();
+ switch (menuItem.getItemId()) {
+ case R.id.mnu_actions_rename:
+ // Rename the fso
+ if (PropertiesModeCallback.this.mOnSelectionListener != null) {
+ CopyMoveActionPolicy.renameFileSystemObject(
+ PropertiesModeCallback.this.mActivity,
+ inputNameDialog.mFso,
+ name,
+ PropertiesModeCallback.this.mOnSelectionListener,
+ PropertiesModeCallback.this.mOnRequestRefreshListener);
+ }
+ break;
+
+ case R.id.mnu_actions_create_link:
+ case R.id.mnu_actions_create_link_global:
+ // Create a link to the fso
+ if (PropertiesModeCallback.this.mOnSelectionListener != null) {
+ NewActionPolicy.createSymlink(
+ PropertiesModeCallback.this.mActivity,
+ inputNameDialog.mFso,
+ name,
+ PropertiesModeCallback.this.mOnSelectionListener,
+ PropertiesModeCallback.this.mOnRequestRefreshListener);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } catch (InflateException e) {
+ //TODO: Catch this exception properly
+ }
+ }
+ });
+ inputNameDialog.show();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/actionmode/SelectionModeCallback.java b/Backbone/src/main/java/me/toolify/backbone/actionmode/SelectionModeCallback.java
new file mode 100644
index 000000000..770bd4521
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/actionmode/SelectionModeCallback.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.actionmode;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.view.ActionMode;
+import android.view.InflateException;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ShareActionProvider;
+import android.widget.TextView;
+
+import java.util.List;
+
+import de.greenrobot.event.EventBus;
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.bus.events.BookmarkRefreshEvent;
+import me.toolify.backbone.bus.events.OpenPropertiesDrawerEvent;
+import me.toolify.backbone.listeners.OnCopyMoveListener;
+import me.toolify.backbone.listeners.OnRequestRefreshListener;
+import me.toolify.backbone.listeners.OnSelectionListener;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.ui.dialogs.InputNameDialog;
+import me.toolify.backbone.ui.policy.BookmarksActionPolicy;
+import me.toolify.backbone.ui.policy.CompressActionPolicy;
+import me.toolify.backbone.ui.policy.CopyMoveActionPolicy;
+import me.toolify.backbone.ui.policy.DeleteActionPolicy;
+import me.toolify.backbone.ui.policy.ExecutionActionPolicy;
+import me.toolify.backbone.ui.policy.InfoActionPolicy;
+import me.toolify.backbone.ui.policy.IntentsActionPolicy;
+import me.toolify.backbone.ui.policy.NavigationActionPolicy;
+import me.toolify.backbone.ui.policy.NewActionPolicy;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.StorageHelper;
+
+public class SelectionModeCallback implements ActionMode.Callback {
+ private MenuItem mActionMoveSelection;
+ private MenuItem mActionDeleteSelection;
+ private MenuItem mActionCompressSelection;
+ private MenuItem mActionCreateLinkGlobal;
+ private MenuItem mActionSendSelection;
+ private MenuItem mActionProperties;
+ private MenuItem mActionOpen;
+ private MenuItem mActionOpenWith;
+ private MenuItem mActionDelete;
+ private MenuItem mActionRename;
+ private MenuItem mActionCompress;
+ private MenuItem mActionExtract;
+ private MenuItem mActionCreateCopy;
+ private MenuItem mActionCreateLink;
+ private MenuItem mActionExecute;
+ private MenuItem mActionSend;
+ private MenuItem mActionAddBookmark;
+ private MenuItem mActionAddShortcut;
+ private MenuItem mActionChecksum;
+
+ private TextView mFileCount;
+ private TextView mFolderCount;
+
+ boolean mClosedByUser = true;
+ private Activity mActivity;
+ private ActionMode mSelectionMode;
+ private Boolean mGlobal;
+ private Boolean mMultiSelection;
+ private final Boolean mSearch;
+ private final Boolean mChRooted;
+
+ /**
+ * @hide
+ */
+ OnRequestRefreshListener mOnRequestRefreshListener;
+ /**
+ * @hide
+ */
+ OnSelectionListener mOnSelectionListener;
+ /**
+ * @hide
+ */
+ OnCopyMoveListener onCopyMoveListener;
+
+ private FileSystemObject mFso;
+
+ /**
+ * Constructor for SelectionModeCallback.
+ *
+ * @param activity The current Activity context
+ * @param search If the call is from search activity
+ */
+ public SelectionModeCallback (Activity activity, Boolean search) {
+ this.mActivity = activity;
+ this.mSearch = search;
+ this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
+ }
+
+ /**
+ * Method that sets the listener for communicate a refresh request.
+ *
+ * @param onRequestRefreshListener The request refresh listener
+ */
+ public void setOnRequestRefreshListener(OnRequestRefreshListener onRequestRefreshListener) {
+ this.mOnRequestRefreshListener = onRequestRefreshListener;
+ }
+
+ /**
+ * Method that sets the listener for requesting selection data
+ *
+ * @param onSelectionListener The request selection data listener
+ */
+ public void setOnSelectionListener(OnSelectionListener onSelectionListener) {
+ this.mOnSelectionListener = onSelectionListener;
+ }
+
+ /**
+ * Method that sets the listener for marking file selections for paste
+ *
+ * @param onCopyMoveListener The request selection data listener
+ */
+ public void setOnCopyMoveListener(OnCopyMoveListener onCopyMoveListener) {
+ this.onCopyMoveListener = onCopyMoveListener;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mSelectionMode = mode;
+
+ MenuInflater inflater = mActivity.getMenuInflater();
+ inflater.inflate(R.menu.actionmode, menu);
+
+ View customTitle = mActivity.getLayoutInflater().inflate(R.layout.navigation_action_mode, null, false);
+ mFileCount = (TextView)customTitle.findViewById(R.id.file_count);
+ mFolderCount = (TextView)customTitle.findViewById(R.id.folder_count);
+ mode.setCustomView(customTitle);
+
+ mActionCreateCopy = menu.findItem(R.id.mnu_actions_copy);
+ mActionMoveSelection = menu.findItem(R.id.mnu_actions_move);
+ mActionDeleteSelection = menu.findItem(R.id.mnu_actions_delete_selection);
+ mActionCompressSelection = menu.findItem(R.id.mnu_actions_compress_selection);
+ mActionCreateLinkGlobal = menu.findItem(R.id.mnu_actions_create_link_global);
+ mActionSendSelection = menu.findItem(R.id.mnu_actions_send_selection);
+ mActionProperties = menu.findItem(R.id.mnu_actions_properties);
+ mActionOpen = menu.findItem(R.id.mnu_actions_open);
+ mActionOpenWith = menu.findItem(R.id.mnu_actions_open_with);
+ mActionDelete = menu.findItem(R.id.mnu_actions_delete);
+ mActionRename = menu.findItem(R.id.mnu_actions_rename);
+ mActionCompress = menu.findItem(R.id.mnu_actions_compress);
+ mActionExtract = menu.findItem(R.id.mnu_actions_extract);
+ mActionCreateLink = menu.findItem(R.id.mnu_actions_create_link);
+ mActionExecute = menu.findItem(R.id.mnu_actions_execute);
+ mActionSend = menu.findItem(R.id.mnu_actions_send);
+ mActionAddBookmark = menu.findItem(R.id.mnu_actions_add_to_bookmarks);
+ mActionAddShortcut = menu.findItem(R.id.mnu_actions_add_shortcut);
+ mActionChecksum = menu.findItem(R.id.mnu_actions_compute_checksum);
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ int folders = 0;
+ int files = 0;
+ String folderCount;
+ String fileCount;
+
+ // Get selection
+ List selection = null;
+ if (this.mOnSelectionListener != null) {
+ selection = this.mOnSelectionListener.onRequestSelectedFiles();
+ }
+
+ // Count selection
+ for (FileSystemObject fso : selection) {
+ if (FileHelper.isDirectory(fso)) {
+ folders++;
+ } else {
+ files++;
+ }
+ }
+
+ // Display selection counts in action mode
+ folderCount = Integer.toString(folders);
+ fileCount = Integer.toString(files);
+ mFolderCount.setText(folderCount);
+ mFileCount.setText(fileCount);
+
+ //TODO: Figure out how to change/fix the mActionCreateLinkGlobal flag
+ this.mGlobal = false;
+
+ // Reset action item visibility
+ mActionMoveSelection.setVisible(true);
+ mActionDeleteSelection.setVisible(true);
+ mActionCompressSelection.setVisible(true);
+ mActionCreateLinkGlobal.setVisible(true);
+ mActionSendSelection.setVisible(true);
+ mActionProperties.setVisible(true);
+ mActionOpen.setVisible(true);
+ mActionOpenWith.setVisible(true);
+ mActionDelete.setVisible(true);
+ mActionRename.setVisible(true);
+ mActionCompress.setVisible(true);
+ mActionExtract.setVisible(true);
+ mActionCreateCopy.setVisible(true);
+ mActionCreateLink.setVisible(true);
+ mActionExecute.setVisible(true);
+ mActionSend.setVisible(true);
+ mActionAddBookmark.setVisible(true);
+ mActionAddShortcut.setVisible(true);
+
+ // Determine the need for single file (not global) and multiple selection (global) operations
+ if (selection.size() == 1) {
+ this.mFso = selection.get(0);
+ this.mMultiSelection = false;
+
+ // Hide multi target actions when only one item is selected
+ mActionDeleteSelection.setVisible(false);
+ mActionCompressSelection.setVisible(false);
+ mActionSendSelection.setVisible(false);
+ } else {
+ this.mMultiSelection = true;
+
+ // Hide single target actions when multiple items are selected
+ mActionProperties.setVisible(false);
+ mActionOpen.setVisible(false);
+ mActionOpenWith.setVisible(false);
+ mActionDelete.setVisible(false);
+ mActionRename.setVisible(false);
+ mActionCompress.setVisible(false);
+ mActionExtract.setVisible(false);
+ mActionCreateLink.setVisible(false);
+ mActionExecute.setVisible(false);
+ mActionSend.setVisible(false);
+ mActionAddBookmark.setVisible(false);
+ mActionAddShortcut.setVisible(false);
+ mActionCreateLinkGlobal.setVisible(false);
+ mActionChecksum.setVisible(false);
+ }
+
+ /*
+ * Single file mode
+ *
+ * Remove the following actions if we are dealing with a single file
+ */
+
+ // Check actions that needs a valid reference
+ if (this.mFso != null) {
+
+ // Hide Send/Open/Open With actions if the fso is folder or a system file
+ if (FileHelper.isDirectory(this.mFso) || FileHelper.isSystemFile(this.mFso)) {
+ mActionOpen.setVisible(false);
+ mActionOpenWith.setVisible(false);
+ mActionSend.setVisible(false);
+ }
+
+ // Create link (not allow in storage volume)
+ if (StorageHelper.isPathInStorageVolume(this.mFso.getFullPath())) {
+ mActionCreateLink.setVisible(false);
+ }
+
+ //Hide Execute action unless the mime/type category is EXEC
+ MimeTypeHelper.MimeTypeCategory category = MimeTypeHelper.getCategory(this.mActivity, this.mFso);
+ if (category.compareTo(MimeTypeHelper.MimeTypeCategory.EXEC) != 0) {
+ mActionExecute.setVisible(false);
+ }
+ }
+
+ // Hide "Add Bookmark" if the fso is the root directory (Already has a bookmark!)
+ if (this.mFso != null && FileHelper.isRootDirectory(this.mFso)) {
+ mActionAddBookmark.setVisible(false);
+ }
+
+ /*
+ * Multi-file mode
+ *
+ * Remove the following actions if we are dealing with a multi-fso selection
+ */
+
+ //- Create link
+ if (this.mGlobal && selection != null) {
+ // Create link (not allow in storage volume)
+ FileSystemObject fso = selection.get(0);
+ if (StorageHelper.isPathInStorageVolume(fso.getFullPath())) {
+ mActionCreateLink.setVisible(false);
+ }
+ } else if (!this.mGlobal) {
+ // Create link (not allow in storage volume)
+ if (StorageHelper.isPathInStorageVolume(this.mFso.getFullPath())) {
+ mActionCreateLink.setVisible(false);
+ }
+ }
+
+ // Hide extract (uncompress/unzip) action for non-supported files
+ if (!this.mMultiSelection && !FileHelper.isSupportedUncompressedFile(this.mFso)) {
+ mActionExtract.setVisible(false);
+ }
+
+ // Send multiple (only regular files)
+ boolean areAllFiles = true;
+ for (FileSystemObject fso : selection) {
+ if (FileHelper.isDirectory(fso)) {
+ areAllFiles = false;
+ break;
+ }
+ }
+ if (!areAllFiles) {
+ mActionSendSelection.setVisible(false);
+ }
+
+ // Hide actions that can't be present when running in unprivileged mode)
+ if (this.mChRooted) {
+ mActionCreateLink.setVisible(false);
+ mActionCreateLinkGlobal.setVisible(false);
+ mActionExecute.setVisible(false);
+
+ // NOTE: This actions are not implemented in chrooted environments. The reason is
+ // that the main target of this application is CyanogenMod (a rooted environment).
+ // Adding this actions requires the use of commons-compress, an external Apache
+ // library that will add more size to the ending apk.
+ // For now, will maintain without implementation. Maybe, in the future.
+ mActionCompress.setVisible(false);
+ mActionCompressSelection.setVisible(false);
+ mActionExtract.setVisible(false);
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
+
+ switch (menuItem.getItemId()) {
+
+ case R.id.mnu_select_all:
+ // Select all items in the visible navigation fragment
+ this.mOnSelectionListener.onSelectAllVisibleItems();
+ break;
+
+ //- Rename
+ case R.id.mnu_actions_rename:
+ if (this.mOnSelectionListener != null) {
+ showFsoInputNameDialog(menuItem, this.mFso, false);
+ finish();
+ return true;
+ }
+ break;
+
+ //- Create link
+ case R.id.mnu_actions_create_link:
+ if (this.mOnSelectionListener != null) {
+ showFsoInputNameDialog(menuItem, this.mFso, true);
+ finish();
+ return true;
+ }
+ break;
+ case R.id.mnu_actions_create_link_global:
+ if (this.mOnSelectionListener != null) {
+ // The selection must be only 1 item
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ if (selection != null && selection.size() == 1) {
+ showFsoInputNameDialog(menuItem, selection.get(0), true);
+ }
+ finish();
+ return true;
+ }
+ break;
+
+ //- Delete
+ case R.id.mnu_actions_delete:
+ DeleteActionPolicy.removeFileSystemObject(
+ this.mActivity,
+ this.mFso,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener,
+ null);
+ finish();
+ break;
+
+ //- Refresh
+ case R.id.mnu_actions_refresh:
+ if (this.mOnRequestRefreshListener != null) {
+ this.mOnRequestRefreshListener.onRequestRefresh(null, false); //Refresh all
+ }
+ break;
+
+ //- Open
+ case R.id.mnu_actions_open:
+ IntentsActionPolicy.openFileSystemObject(
+ this.mActivity, this.mFso, false, null, null);
+ finish();
+ break;
+ //- Open with
+ case R.id.mnu_actions_open_with:
+ IntentsActionPolicy.openFileSystemObject(
+ this.mActivity, this.mFso, true, null, null);
+ finish();
+ break;
+
+ //- Execute
+ case R.id.mnu_actions_execute:
+ ExecutionActionPolicy.execute(this.mActivity, this.mFso);
+ finish();
+ break;
+
+ //- Send
+ case R.id.mnu_actions_send:
+ IntentsActionPolicy.sendFileSystemObject(
+ this.mActivity, this.mFso, null, null);
+ finish();
+ break;
+ case R.id.mnu_actions_send_selection:
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ if (selection.size() == 1) {
+ IntentsActionPolicy.sendFileSystemObject(
+ this.mActivity, selection.get(0), null, null);
+ } else {
+ IntentsActionPolicy.sendMultipleFileSystemObject(
+ this.mActivity, selection, null, null);
+ }
+ finish();
+ }
+ break;
+
+ //- Create copy
+ case R.id.mnu_actions_copy:
+ // Create a copy of the fso
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ CopyMoveActionPolicy.createCopyFileSystemObject(
+ selection,
+ this.onCopyMoveListener);
+ // Make sure that the main menu updates and shows the paste button.
+ mActivity.invalidateOptionsMenu();
+ finish();
+ }
+ break;
+
+ // Move selection
+ case R.id.mnu_actions_move:
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ CopyMoveActionPolicy.createMoveFileSystemObject(
+ selection,
+ this.onCopyMoveListener);
+ // Make sure that the main menu updates and shows the paste button.
+ mActivity.invalidateOptionsMenu();
+ finish();
+ }
+ break;
+
+ // Delete selection
+ case R.id.mnu_actions_delete_selection:
+ if (this.mOnSelectionListener != null) {
+ List selection =
+ this.mOnSelectionListener.onRequestSelectedFiles();
+ DeleteActionPolicy.removeFileSystemObjects(
+ this.mActivity,
+ selection,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener,
+ null);
+ finish();
+ }
+ break;
+
+ //- Uncompress
+ case R.id.mnu_actions_extract:
+ CompressActionPolicy.uncompress(
+ this.mActivity,
+ this.mFso,
+ this.mOnRequestRefreshListener);
+ break;
+ //- Checksum
+ case R.id.mnu_actions_compute_checksum:
+ InfoActionPolicy.showComputeChecksumDialog(
+ this.mActivity,
+ this.mFso);
+ break;
+ //- Compress
+ case R.id.mnu_actions_compress:
+ if (this.mOnSelectionListener != null) {
+ CompressActionPolicy.compress(
+ this.mActivity,
+ this.mFso,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener);
+ finish();
+ }
+ break;
+ case R.id.mnu_actions_compress_selection:
+ if (this.mOnSelectionListener != null) {
+ CompressActionPolicy.compress(
+ this.mActivity,
+ this.mOnSelectionListener,
+ this.mOnRequestRefreshListener);
+ finish();
+ }
+ break;
+
+ //- Add to bookmarks
+ case R.id.mnu_actions_add_to_bookmarks:
+ BookmarksActionPolicy.addToBookmarks(this.mActivity, this.mFso);
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ break;
+
+ //- Add shortcut
+ case R.id.mnu_actions_add_shortcut:
+ IntentsActionPolicy.createShortcut(this.mActivity, this.mFso);
+ break;
+
+ //- Properties
+ case R.id.mnu_actions_properties:
+ case R.id.mnu_actions_properties_current_folder:
+ EventBus.getDefault().post(new OpenPropertiesDrawerEvent(this.mFso));
+ break;
+
+ //- Navigate to parent
+ case R.id.mnu_actions_open_parent_folder:
+ NavigationActionPolicy.openParentFolder(
+ this.mActivity, this.mFso, this.mOnRequestRefreshListener);
+ break;
+
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ // Clear this before onDeselectAll() to prevent onDeselectAll() from
+ // trying to close the contextual mode again.
+ mSelectionMode = null;
+ mOnSelectionListener.onDeselectAll();
+ }
+
+ public void finish() {
+ mSelectionMode.finish();
+ }
+
+ public void setClosedByUser(boolean closedByUser) {
+ this.mClosedByUser = closedByUser;
+ }
+
+ public boolean inSelectionMode() {
+ return mSelectionMode != null;
+ }
+
+ public void refresh() {
+ mSelectionMode.invalidate();
+ }
+
+ /**
+ * Method that show a new dialog for input a name for an existing fso.
+ *
+ * @param menuItem The item menu associated
+ * @param fso The file system object
+ * @param allowFsoName If allow that the name of the fso will be returned
+ */
+ private void showFsoInputNameDialog(
+ final MenuItem menuItem, final FileSystemObject fso, final boolean allowFsoName) {
+
+ //Show the input name dialog
+ final InputNameDialog inputNameDialog =
+ new InputNameDialog(
+ this.mActivity,
+ this.mOnSelectionListener.onRequestCurrentItems(),
+ fso,
+ allowFsoName,
+ menuItem.getTitle().toString());
+ inputNameDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ //Retrieve the name an execute the action
+ try {
+ String name = inputNameDialog.getName();
+ switch (menuItem.getItemId()) {
+ case R.id.mnu_actions_rename:
+ // Rename the fso
+ if (SelectionModeCallback.this.mOnSelectionListener != null) {
+ CopyMoveActionPolicy.renameFileSystemObject(
+ SelectionModeCallback.this.mActivity,
+ inputNameDialog.mFso,
+ name,
+ SelectionModeCallback.this.mOnSelectionListener,
+ SelectionModeCallback.this.mOnRequestRefreshListener);
+ }
+ break;
+
+ case R.id.mnu_actions_create_link:
+ case R.id.mnu_actions_create_link_global:
+ // Create a link to the fso
+ if (SelectionModeCallback.this.mOnSelectionListener != null) {
+ NewActionPolicy.createSymlink(
+ SelectionModeCallback.this.mActivity,
+ inputNameDialog.mFso,
+ name,
+ SelectionModeCallback.this.mOnSelectionListener,
+ SelectionModeCallback.this.mOnRequestRefreshListener);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ } catch (InflateException e) {
+ //TODO: Catch this exception properly
+ }
+ }
+ });
+ inputNameDialog.show();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/AbstractNavigationActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/AbstractNavigationActivity.java
new file mode 100644
index 000000000..9994151b0
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/AbstractNavigationActivity.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.activities;
+
+import android.app.Activity;
+
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.fragments.NavigationFragment;
+import me.toolify.backbone.ui.widgets.BreadcrumbListener;
+
+public abstract class AbstractNavigationActivity extends Activity
+ implements BreadcrumbListener {
+
+ // The flag indicating whether or not the application is just starting up.
+ // Used in initializing the action bar's breadcrumb once the fragments have
+ // finished loading.
+ private boolean mFirstRun = true;
+
+ /**
+ * Determine whether the "just started up" flag is true or not. Used to perform
+ * actions on/based on the first (and only visible) navigationFragment loaded.
+ */
+ public boolean isFirstRun() {
+ return mFirstRun;
+ }
+
+ /**
+ * Change the app's "just started up" flag
+ */
+ public void setFirstRun(boolean firstRun) {
+ this.mFirstRun = firstRun;
+ }
+
+ /**
+ * Method that updates the titlebar of the activity or dialog
+ */
+ public abstract void updateTitleActionBar();
+
+ /**
+ * Method called when a controlled exit is required
+ * @hide
+ */
+ public void exit() {
+ try {
+ FileManagerApplication.destroyBackgroundConsole();
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ try {
+ ConsoleBuilder.destroyConsole();
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ finish();
+ }
+
+ /**
+ * Method that associates a breadcrumb with a {@link me.toolify.backbone.fragments.NavigationFragment}
+ * based on their shared position. This method does not create breadcrumbs, so it should throw
+ * an exception if a breadcrumb with the specified position does not exist.
+ *
+ * @param mPosition the position of the fragment
+ * @param fragment the fragment recieving the breadcrumb association
+ */
+ public abstract void pairBreadcrumb(int mPosition, NavigationFragment fragment);
+}
diff --git a/src/com/cyanogenmod/filemanager/activities/ChangeLogActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/ChangeLogActivity.java
similarity index 93%
rename from src/com/cyanogenmod/filemanager/activities/ChangeLogActivity.java
rename to Backbone/src/main/java/me/toolify/backbone/activities/ChangeLogActivity.java
index ae3a7c8f1..9f9d44210 100644
--- a/src/com/cyanogenmod/filemanager/activities/ChangeLogActivity.java
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/ChangeLogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.activities;
+package me.toolify.backbone.activities;
import android.app.Activity;
import android.app.AlertDialog;
@@ -28,11 +28,11 @@
import android.os.Bundle;
import android.util.Log;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
-import com.cyanogenmod.filemanager.util.DialogHelper;
+import me.toolify.backbone.R;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.util.DialogHelper;
import java.io.InputStream;
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/EditorActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/EditorActivity.java
new file mode 100644
index 000000000..c7199b637
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/EditorActivity.java
@@ -0,0 +1,1431 @@
+/*
+ * 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 me.toolify.backbone.activities;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.preference.PreferenceActivity;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.*;
+import android.widget.EditText;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.TextView.BufferType;
+import android.widget.Toast;
+
+import me.toolify.backbone.util.HexDump;
+import me.toolify.backbone.R;
+import me.toolify.backbone.activities.preferences.EditorPreferenceFragment;
+import me.toolify.backbone.activities.preferences.EditorSHColorSchemePreferenceFragment;
+import me.toolify.backbone.activities.preferences.SettingsPreferences;
+import com.ash.syntaxhighlight.HighlightColors;
+import com.ash.syntaxhighlight.ISyntaxHighlightResourcesResolver;
+import com.ash.syntaxhighlight.SyntaxHighlightFactory;
+import com.ash.syntaxhighlight.SyntaxHighlightProcessor;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.WriteExecutable;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.widgets.ButtonItem;
+import me.toolify.backbone.util.CommandHelper;
+import me.toolify.backbone.util.DialogHelper;
+import me.toolify.backbone.util.ExceptionUtil;
+import me.toolify.backbone.util.ExceptionUtil.OnRelaunchCommandResult;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.ResourcesHelper;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ * An internal activity for view and edit files.
+ */
+public class EditorActivity extends Activity implements TextWatcher {
+
+ private static final String TAG = "EditorActivity"; //$NON-NLS-1$
+
+ private static boolean DEBUG = false;
+
+ private static final int WRITE_RETRIES = 3;
+
+ private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
+ applyTheme();
+ return;
+ }
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
+ // The settings has changed
+ String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
+ if (key != null) {
+ final EditorActivity activity = EditorActivity.this;
+
+ // No suggestions
+ if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId()) == 0) {
+ // Ignore in binary files
+ if (activity.mBinary) return;
+
+ // Do we have a different setting?
+ boolean noSuggestionsSetting =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.
+ getDefaultValue()).booleanValue());
+ if (noSuggestionsSetting != activity.mNoSuggestions) {
+ activity.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ toggleNoSuggestions();
+ }
+ });
+ }
+
+ // Word wrap
+ } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId()) == 0) {
+ // Ignore in binary files
+ if (activity.mBinary) return;
+
+ // Do we have a different setting?
+ boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.
+ getDefaultValue()).booleanValue());
+ if (wordWrapSetting != activity.mWordWrap) {
+ activity.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ toggleWordWrap();
+ }
+ });
+ }
+
+ // Syntax highlight
+ // Default theme color scheme
+ // Color scheme
+ } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId()) == 0) {
+ // Ignore in binary files
+ if (activity.mBinary) return;
+
+ // Do we have a different setting?
+ boolean syntaxHighlightSetting =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.
+ getDefaultValue()).booleanValue());
+ if (syntaxHighlightSetting != activity.mSyntaxHighlight) {
+ activity.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ toggleSyntaxHighlight();
+ }
+ });
+ }
+
+ } else if (key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()) == 0 ||
+ key.compareTo(FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId()) == 0 ) {
+ // Ignore in binary files
+ if (activity.mBinary) return;
+
+ // Reload the syntax highlight
+ activity.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ reloadSyntaxHighlight();
+ }
+ });
+ }
+ }
+ return;
+ }
+ }
+ }
+ };
+
+ /**
+ * Internal interface to notify progress update
+ */
+ private interface OnProgressListener {
+ void onProgress(int progress);
+ }
+
+ /**
+ * An internal listener for read a file
+ */
+ private class AsyncReader implements AsyncResultListener {
+
+ final Object mSync = new Object();
+ ByteArrayOutputStream mByteBuffer = null;
+ SpannableStringBuilder mBuffer = null;
+ Exception mCause;
+ long mSize;
+ FileSystemObject mReadFso;
+ OnProgressListener mListener;
+
+ /**
+ * Constructor of AsyncReader. For enclosing access.
+ */
+ public AsyncReader() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncStart() {
+ this.mByteBuffer = new ByteArrayOutputStream((int)this.mReadFso.getSize());
+ this.mSize = 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncEnd(boolean cancelled) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncExitCode(int exitCode) {
+ synchronized (this.mSync) {
+ this.mSync.notify();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPartialResult(Object result) {
+ try {
+ if (result == null) return;
+ byte[] partial = (byte[])result;
+
+ // Check if the file is a binary file. In this case the editor
+ // is read-only
+ if (!EditorActivity.this.mReadOnly) {
+ for (int i = 0; i < partial.length-1; i++) {
+ if (!isPrintableCharacter((char)partial[i])) {
+ EditorActivity.this.mBinary = true;
+ EditorActivity.this.mReadOnly = true;
+ break;
+ }
+ }
+ }
+
+ this.mByteBuffer.write(partial, 0, partial.length);
+ this.mSize += partial.length;
+ if (this.mListener != null && this.mReadFso != null) {
+ int progress = 0;
+ if (this.mReadFso.getSize() != 0) {
+ progress = (int)((this.mSize*100) / this.mReadFso.getSize());
+ }
+ this.mListener.onProgress(progress);
+ }
+ } catch (Exception e) {
+ this.mCause = e;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onException(Exception cause) {
+ this.mCause = cause;
+ }
+ }
+
+ /**
+ * An internal listener for write a file
+ */
+ private class AsyncWriter implements AsyncResultListener {
+
+ Exception mCause;
+
+ /**
+ * Constructor of AsyncWriter. For enclosing access.
+ */
+ public AsyncWriter() {
+ super();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncStart() {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncEnd(boolean cancelled) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAsyncExitCode(int exitCode) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPartialResult(Object result) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onException(Exception cause) {
+ this.mCause = cause;
+ }
+ }
+
+ /**
+ * An internal class to resolve resources for the syntax highlight library.
+ * @hide
+ */
+ class ResourcesResolver implements ISyntaxHighlightResourcesResolver {
+ @Override
+ public CharSequence getString(String id, String resid) {
+ return EditorActivity.this.getString(
+ ResourcesHelper.getIdentifier(
+ EditorActivity.this.getResources(), "string", resid)); //$NON-NLS-1$
+ }
+
+ @Override
+ public int getInteger(String id, String resid, int def) {
+ return EditorActivity.this.getResources().
+ getInteger(
+ ResourcesHelper.getIdentifier(
+ EditorActivity.this.getResources(), "integer", resid)); //$NON-NLS-1$
+ }
+
+ @Override
+ public int getColor(String id, String resid, int def) {
+ final Context ctx = EditorActivity.this;
+ try {
+ // Is default theme color scheme enabled?
+ if (isDefaultThemeColorScheme()) {
+ return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid);
+ }
+
+ // Use the user-defined settings
+ int[] colors = getUserColorScheme();
+ HighlightColors[] schemeColors = HighlightColors.values();
+ int cc = schemeColors.length;
+ int cc2 = colors.length;
+ for (int i = 0; i < cc; i++) {
+ if (schemeColors[i].getId().compareTo(id) == 0) {
+ if (cc2 >= i) {
+ // User-defined
+ return colors[i];
+ }
+
+ // Theme default
+ return ThemeManager.getCurrentTheme(ctx).getColor(ctx, resid);
+ }
+
+ }
+
+ } catch (Exception ex) {
+ // Resource not found
+ }
+ return def;
+ }
+
+ /**
+ * Method that returns if we should return the default theme color scheme or not
+ *
+ * @return boolean Whether return the default theme color scheme or not
+ */
+ private boolean isDefaultThemeColorScheme() {
+ Boolean defaultValue =
+ (Boolean)FileManagerSettings.
+ SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getDefaultValue();
+ return Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId(),
+ defaultValue.booleanValue());
+ }
+
+ /**
+ * Method that returns the user-defined color scheme
+ *
+ * @return int[] The user-defined color scheme
+ */
+ private int[] getUserColorScheme() {
+ String defaultValue =
+ (String)FileManagerSettings.
+ SETTINGS_EDITOR_SH_COLOR_SCHEME.getDefaultValue();
+ String value = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(),
+ defaultValue);
+ return EditorSHColorSchemePreferenceFragment.toColorShemeArray(value);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ FileSystemObject mFso;
+
+ private int mBufferSize;
+ private int mMaxFileSize;
+
+ /**
+ * @hide
+ */
+ boolean mDirty;
+ /**
+ * @hide
+ */
+ boolean mReadOnly;
+ /**
+ * @hide
+ */
+ boolean mBinary;
+
+ /**
+ * @hide
+ */
+ TextView mTitle;
+ /**
+ * @hide
+ */
+ EditText mEditor;
+ /**
+ * @hide
+ */
+ View mProgress;
+ /**
+ * @hide
+ */
+ ProgressBar mProgressBar;
+ /**
+ * @hide
+ */
+ TextView mProgressBarMsg;
+ /**
+ * @hide
+ */
+ ButtonItem mSave;
+ MenuItem mSaveAction;
+ MenuItem mWordWrapAction;
+ MenuItem mNoSuggestionsAction;
+ MenuItem mSyntaxHighlightAction;
+
+ // No suggestions status
+ /**
+ * @hide
+ */
+ boolean mNoSuggestions;
+
+ // Word wrap status
+ private ViewGroup mWordWrapView;
+ private ViewGroup mNoWordWrapView;
+ /**
+ * @hide
+ */
+ boolean mWordWrap;
+
+ // Syntax highlight status
+ /**
+ * @hide
+ */
+ boolean mSyntaxHighlight;
+ /**
+ * @hide
+ */
+ SyntaxHighlightProcessor mSyntaxHighlightProcessor;
+ private int mEditStart;
+ private int mEditEnd;
+
+ private View mOptionsAnchorView;
+
+ private final Object mExecSync = new Object();
+
+ /**
+ * @hide
+ */
+ Handler mHandler;
+
+ private static final char[] VALID_NON_PRINTABLE_CHARS = {' ', '\t', '\r', '\n'};
+
+ /**
+ * @hide
+ */
+ String mHexLineSeparator;
+
+ /**
+ * Intent extra parameter for the path of the file to open.
+ */
+ public static final String EXTRA_OPEN_FILE = "extra_open_file"; //$NON-NLS-1$
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCreate(Bundle state) {
+ if (DEBUG) {
+ Log.d(TAG, "EditorActivity.onCreate"); //$NON-NLS-1$
+ }
+
+ this.mHandler = new Handler();
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+ filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
+ registerReceiver(this.mNotificationReceiver, filter);
+
+ // Generate a random separator
+ this.mHexLineSeparator = UUID.randomUUID().toString();
+
+ //Set the main layout of the activity
+ setContentView(R.layout.editor);
+
+ // Get the limit vars
+ this.mBufferSize =
+ getApplicationContext().getResources().getInteger(R.integer.buffer_size);
+ this.mMaxFileSize =
+ getApplicationContext().getResources().getInteger(R.integer.editor_max_file_size);
+
+ //Initialize
+ initTitleActionBar();
+ initLayout();
+
+ // Apply the theme
+ applyTheme();
+
+ // Initialize the console
+ initializeConsole();
+
+ // Read the file
+ readFile();
+
+ //Save state
+ super.onCreate(state);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "EditorActivity.onDestroy"); //$NON-NLS-1$
+ }
+
+ // Unregister the receiver
+ try {
+ unregisterReceiver(this.mNotificationReceiver);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Method that initializes the titlebar of the activity.
+ */
+ private void initTitleActionBar() {
+ //Configure the action bar options
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setTitle(R.string.editor);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.editor, menu);
+ mSaveAction = menu.findItem(R.id.mnu_save);
+ mWordWrapAction = menu.findItem(R.id.mnu_word_wrap);
+ mNoSuggestionsAction = menu.findItem(R.id.mnu_no_suggestions);
+ mSyntaxHighlightAction = menu.findItem(R.id.mnu_syntax_highlight);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ mWordWrapAction.setChecked(this.mWordWrap);
+ mNoSuggestionsAction.setChecked(mNoSuggestions);
+ mSyntaxHighlightAction.setChecked(mSyntaxHighlight);
+ mSaveAction.setVisible(this.mDirty);
+
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ if ((getActionBar().getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP)
+ == ActionBar.DISPLAY_HOME_AS_UP) {
+ checkDirtyState();
+ }
+ break;
+ case R.id.mnu_save:
+ // Save the file
+ checkAndWrite();
+ break;
+ case R.id.mnu_no_suggestions:
+ toggleNoSuggestions();
+ break;
+ case R.id.mnu_word_wrap:
+ toggleWordWrap();
+ break;
+ case R.id.mnu_syntax_highlight:
+ toggleSyntaxHighlight();
+ break;
+ case R.id.mnu_settings:
+ //Settings
+ Intent settings = new Intent(EditorActivity.this, SettingsPreferences.class);
+ settings.putExtra(
+ PreferenceActivity.EXTRA_SHOW_FRAGMENT,
+ EditorPreferenceFragment.class.getName());
+ startActivity(settings);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Method that initializes the layout and components of the activity.
+ */
+ private void initLayout() {
+ this.mEditor = (EditText)findViewById(R.id.editor);
+ this.mEditor.setText(null);
+ this.mEditor.addTextChangedListener(this);
+ this.mEditor.setEnabled(false);
+ this.mWordWrapView = (ViewGroup)findViewById(R.id.editor_word_wrap_view);
+ this.mNoWordWrapView = (ViewGroup)findViewById(R.id.editor_no_word_wrap_view);
+ this.mWordWrapView.setVisibility(View.VISIBLE);
+ this.mNoWordWrapView.setVisibility(View.GONE);
+
+ this.mNoSuggestions = false;
+ this.mWordWrap = true;
+ this.mSyntaxHighlight = true;
+
+ // Load the no suggestions setting
+ boolean noSuggestionsSetting = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.
+ getDefaultValue()).booleanValue());
+ if (noSuggestionsSetting != this.mNoSuggestions) {
+ toggleNoSuggestions();
+ }
+
+ // Load the word wrap setting
+ boolean wordWrapSetting = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.
+ getDefaultValue()).booleanValue());
+ if (wordWrapSetting != this.mWordWrap) {
+ toggleWordWrap();
+ }
+
+ // Load the syntax highlight setting
+ boolean syntaxHighlighSetting = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.
+ getDefaultValue()).booleanValue());
+ if (syntaxHighlighSetting != this.mSyntaxHighlight) {
+ toggleSyntaxHighlight();
+ }
+
+ this.mProgress = findViewById(R.id.editor_progress);
+ this.mProgressBar = (ProgressBar)findViewById(R.id.editor_progress_bar);
+ this.mProgressBarMsg = (TextView)findViewById(R.id.editor_progress_msg);
+ }
+
+ /**
+ * Method that toggle the no suggestions property of the editor
+ * @hide
+ */
+ /**package**/ void toggleNoSuggestions() {
+ synchronized (this.mExecSync) {
+ int type = InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE |
+ InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
+ if (!this.mNoSuggestions) {
+ type |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+ }
+ this.mEditor.setInputType(type);
+ this.mNoSuggestions = !this.mNoSuggestions;
+ }
+ }
+
+ /**
+ * Method that toggle the word wrap property of the editor
+ * @hide
+ */
+ /**package**/ void toggleWordWrap() {
+ synchronized (this.mExecSync) {
+ ViewGroup vSrc = this.mWordWrap ? this.mWordWrapView : this.mNoWordWrapView;
+ ViewGroup vDst = this.mWordWrap ? this.mNoWordWrapView : this.mWordWrapView;
+ ViewGroup vSrcParent = this.mWordWrap
+ ? this.mWordWrapView
+ : (ViewGroup)this.mNoWordWrapView.getChildAt(0);
+ ViewGroup vDstParent = this.mWordWrap
+ ? (ViewGroup)this.mNoWordWrapView.getChildAt(0)
+ : this.mWordWrapView;
+ vSrc.setVisibility(View.GONE);
+ vSrcParent.removeView(this.mEditor);
+ vDstParent.addView(this.mEditor);
+ vDst.setVisibility(View.VISIBLE);
+ vDst.scrollTo(0, 0);
+ this.mWordWrap = !this.mWordWrap;
+ }
+ }
+
+ /**
+ * Method that toggles the syntax highlight property of the editor
+ * @hide
+ */
+ /**package**/ void toggleSyntaxHighlight() {
+ synchronized (this.mExecSync) {
+ if (this.mSyntaxHighlightProcessor != null) {
+ try {
+ if (this.mSyntaxHighlight) {
+ this.mSyntaxHighlightProcessor.clear(this.mEditor.getText());
+ } else {
+ this.mSyntaxHighlightProcessor.process(this.mEditor.getText());
+ }
+ } catch (Exception ex) {
+ // An error in a syntax library, should not break down app.
+ Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
+ }
+ }
+
+ this.mSyntaxHighlight = !this.mSyntaxHighlight;
+ }
+ }
+
+ /**
+ * Method that reloads the syntax highlight of the current file
+ * @hide
+ */
+ /**package**/ void reloadSyntaxHighlight() {
+ synchronized (this.mExecSync) {
+ if (this.mSyntaxHighlightProcessor != null) {
+ try {
+ this.mSyntaxHighlightProcessor.initialize();
+ this.mSyntaxHighlightProcessor.process(this.mEditor.getText());
+ } catch (Exception ex) {
+ // An error in a syntax library, should not break down app.
+ Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ checkDirtyState();
+ return true;
+ default:
+ return super.onKeyUp(keyCode, event);
+ }
+ }
+
+ /**
+ * Method that initializes a console
+ */
+ private boolean initializeConsole() {
+ try {
+ ConsoleBuilder.getConsole(this);
+ // There is a console allocated. Use it.
+ return true;
+ } catch (Throwable _throw) {
+ // Capture the exception
+ ExceptionUtil.translateException(this, _throw, false, true);
+ }
+ return false;
+ }
+
+ /**
+ * Method that reads the requested file
+ */
+ private void readFile() {
+ // For now editor is not dirty and editable.
+ setDirty(false);
+ this.mBinary = false;
+
+ // Check for a valid action
+ String action = getIntent().getAction();
+ if (action == null ||
+ (action.compareTo(Intent.ACTION_VIEW) != 0) &&
+ (action.compareTo(Intent.ACTION_EDIT) != 0)) {
+ DialogHelper.showToast(
+ this, R.string.editor_invalid_file_msg, Toast.LENGTH_SHORT);
+ return;
+ }
+ // This var should be set depending on ACTION_VIEW or ACTION_EDIT action, but for
+ // better compatibility, IntentsActionPolicy use always ACTION_VIEW, so we have
+ // to ignore this check here
+ this.mReadOnly = false;
+
+ // Read the intent and check that is has a valid request
+ String path = getIntent().getData().getPath();
+ if (path == null || path.length() == 0) {
+ DialogHelper.showToast(
+ this, R.string.editor_invalid_file_msg, Toast.LENGTH_SHORT);
+ return;
+ }
+
+ // Set the title of the dialog
+ File f = new File(path);
+ getActionBar().setTitle(f.getName());
+
+ // Check that the file exists (the real file, not the symlink)
+ try {
+ this.mFso = CommandHelper.getFileInfo(this, path, true, null);
+ if (this.mFso == null) {
+ DialogHelper.showToast(
+ this, R.string.editor_file_not_found_msg, Toast.LENGTH_SHORT);
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to get file reference", e); //$NON-NLS-1$
+ DialogHelper.showToast(
+ this, R.string.editor_file_not_found_msg, Toast.LENGTH_SHORT);
+ return;
+ }
+
+ // Check that we can handle the length of the file (by device)
+ if (this.mMaxFileSize < this.mFso.getSize()) {
+ DialogHelper.showToast(
+ this, R.string.editor_file_exceed_size_msg, Toast.LENGTH_SHORT);
+ return;
+ }
+
+ // Get the syntax highlight processor
+ SyntaxHighlightFactory shpFactory =
+ SyntaxHighlightFactory.getDefaultFactory(new ResourcesResolver());
+ this.mSyntaxHighlightProcessor = shpFactory.getSyntaxHighlightProcessor(f);
+ if (this.mSyntaxHighlightProcessor != null) {
+ this.mSyntaxHighlightProcessor.initialize();
+ }
+
+ // Check that we have read access
+ try {
+ FileHelper.ensureReadAccess(
+ ConsoleBuilder.getConsole(this),
+ this.mFso,
+ null);
+
+ // Read the file in background
+ asyncRead();
+
+ } catch (Exception ex) {
+ ExceptionUtil.translateException(
+ this, ex, false, true, new OnRelaunchCommandResult() {
+ @Override
+ public void onSuccess() {
+ // Read the file in background
+ asyncRead();
+ }
+
+ @Override
+ public void onFailed(Throwable cause) {
+ finish();
+ }
+
+ @Override
+ public void onCancelled() {
+ finish();
+ }
+ });
+ }
+ }
+
+ /**
+ * Method that does the read of the file in background
+ * @hide
+ */
+ void asyncRead() {
+ // Do the load of the file
+ AsyncTask mReadTask =
+ new AsyncTask() {
+
+ private Exception mCause;
+ private AsyncReader mReader;
+ private boolean changeToBinaryMode;
+ private boolean changeToDisplaying;
+
+ @Override
+ protected void onPreExecute() {
+ // Show the progress
+ this.changeToBinaryMode = false;
+ this.changeToDisplaying = false;
+ doProgress(true, 0);
+ }
+
+ @Override
+ protected Boolean doInBackground(FileSystemObject... params) {
+ final EditorActivity activity = EditorActivity.this;
+
+ // Only one argument (the file to open)
+ FileSystemObject fso = params[0];
+ this.mCause = null;
+
+ // Read the file in an async listener
+ try {
+ while (true) {
+ // Configure the reader
+ this.mReader = new AsyncReader();
+ this.mReader.mReadFso = fso;
+ this.mReader.mListener = new OnProgressListener() {
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public void onProgress(int progress) {
+ publishProgress(Integer.valueOf(progress));
+ }
+ };
+
+ // Execute the command (read the file)
+ CommandHelper.read(activity, fso.getFullPath(), this.mReader, null);
+
+ // Wait for
+ synchronized (this.mReader.mSync) {
+ this.mReader.mSync.wait();
+ }
+
+ // 100%
+ publishProgress(new Integer(100));
+
+ // Check if the read was successfully
+ if (this.mReader.mCause != null) {
+ this.mCause = this.mReader.mCause;
+ return Boolean.FALSE;
+ }
+ break;
+ }
+
+ // Now we have the byte array with all the data. is a binary file?
+ // Then dump them byte array to hex dump string (only if users settings
+ // to dump file)
+ boolean hexDump =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.
+ getDefaultValue()).booleanValue());
+ if (activity.mBinary && hexDump) {
+ // we do not use the Hexdump helper class, because we need to show the
+ // progress of the dump process
+ final String data = toHexPrintableString(
+ toHexDump(
+ this.mReader.mByteBuffer.toByteArray()));
+ this.mReader.mBuffer = new SpannableStringBuilder(data);
+ Log.i(TAG, "Bytes read: " + data.getBytes().length); //$NON-NLS-1$
+ } else {
+ final String data = new String(this.mReader.mByteBuffer.toByteArray());
+ this.mReader.mBuffer = new SpannableStringBuilder(data);
+ Log.i(TAG, "Bytes read: " + data.getBytes().length); //$NON-NLS-1$
+ }
+ this.mReader.mByteBuffer = null;
+
+ // 100%
+ this.changeToDisplaying = true;
+ publishProgress(new Integer(0));
+
+ } catch (Exception e) {
+ this.mCause = e;
+ return Boolean.FALSE;
+ }
+
+ return Boolean.TRUE;
+ }
+
+ @Override
+ protected void onProgressUpdate(Integer... values) {
+ // Do progress
+ doProgress(true, values[0].intValue());
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ final EditorActivity activity = EditorActivity.this;
+
+ // Is error?
+ if (!result.booleanValue()) {
+ if (this.mCause != null) {
+ ExceptionUtil.translateException(activity, this.mCause);
+ activity.mEditor.setEnabled(false);
+ }
+ } else {
+ // Now we have the buffer, set the text of the editor
+ if (activity.mBinary) {
+ activity.mEditor.setText(
+ this.mReader.mBuffer, BufferType.NORMAL);
+ } else {
+ activity.mEditor.setText(
+ this.mReader.mBuffer, BufferType.EDITABLE);
+
+ // Highlight editor text syntax
+ if (activity.mSyntaxHighlight &&
+ activity.mSyntaxHighlightProcessor != null) {
+ try {
+ activity.mSyntaxHighlightProcessor.process(
+ activity.mEditor.getText());
+ } catch (Exception ex) {
+ // An error in a syntax library, should not break down app.
+ Log.e(TAG, "Syntax highlight failed.", ex); //$NON-NLS-1$
+ }
+ }
+ }
+ this.mReader.mBuffer = null; //Cleanup
+ setDirty(false);
+ activity.mEditor.setEnabled(!activity.mReadOnly);
+
+ // Notify read-only mode
+ if (activity.mReadOnly) {
+ DialogHelper.showToast(
+ activity,
+ R.string.editor_read_only_mode,
+ Toast.LENGTH_SHORT);
+ }
+ }
+
+ doProgress(false, 0);
+ }
+
+ @Override
+ protected void onCancelled() {
+ // Hide the progress
+ doProgress(false, 0);
+ }
+
+ /**
+ * Method that update the progress status
+ *
+ * @param visible If the progress bar need to be hidden
+ * @param progress The progress
+ */
+ private void doProgress(boolean visible, int progress) {
+ final EditorActivity activity = EditorActivity.this;
+
+ // Show the progress bar
+ activity.mProgressBar.setProgress(progress);
+ activity.mProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+ if (this.changeToBinaryMode) {
+ // Hexdump always in nowrap mode
+ if (activity.mWordWrap) {
+ activity.toggleWordWrap();
+ }
+ // Hexdump always has no syntax highlight
+ if (activity.mSyntaxHighlight) {
+ activity.toggleSyntaxHighlight();
+ }
+
+ // Show hex dumping text
+ activity.mProgressBarMsg.setText(R.string.dumping_message);
+ applyHexViewerTheme();
+ this.changeToBinaryMode = false;
+ }
+ else if (this.changeToDisplaying) {
+ activity.mProgressBarMsg.setText(R.string.displaying_message);
+ this.changeToDisplaying = false;
+ }
+ }
+
+ /**
+ * Create a hex dump of the data while show progress to user
+ *
+ * @param data The data to hex dump
+ * @return StringBuilder The hex dump buffer
+ */
+ private String toHexDump(byte[] data) {
+ //Change to binary mode
+ this.changeToBinaryMode = true;
+
+ // Start progress
+ publishProgress(Integer.valueOf(0));
+
+ // Calculate max dir size
+ int length = data.length;
+
+ final int DISPLAY_SIZE = 16; // Bytes per line
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ byte[] line = new byte[DISPLAY_SIZE];
+ int read = 0;
+ int offset = 0;
+ StringBuilder sb = new StringBuilder();
+ while ((read = bais.read(line, 0, DISPLAY_SIZE)) != -1) {
+ //offset dump(16) data\n
+ String linedata = new String(line, 0, read);
+ sb.append(HexDump.toHexString(offset));
+ sb.append(" "); //$NON-NLS-1$
+ String hexDump = HexDump.toHexString(line, 0, read);
+ if (hexDump.length() != (DISPLAY_SIZE * 2)) {
+ char[] array = new char[(DISPLAY_SIZE * 2) - hexDump.length()];
+ Arrays.fill(array, ' ');
+ hexDump += new String(array);
+ }
+ sb.append(hexDump);
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(linedata);
+ sb.append(EditorActivity.this.mHexLineSeparator);
+ offset += DISPLAY_SIZE;
+ if (offset % 5 == 0) {
+ publishProgress(Integer.valueOf((offset * 100) / length));
+ }
+ }
+
+ // End of the dump process
+ publishProgress(Integer.valueOf(100));
+
+ return sb.toString();
+ }
+
+ /**
+ * Method that converts to a visual printable hex string
+ *
+ * @param string The string to check
+ */
+ private String toHexPrintableString(String string) {
+ // Remove characters without visual representation
+ final String REPLACED_SYMBOL = "."; //$NON-NLS-1$
+ final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
+ String printable = string.replaceAll("\\p{Cntrl}", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll("[^\\p{Print}]", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll("\\p{C}", REPLACED_SYMBOL); //$NON-NLS-1$
+ printable = printable.replaceAll(EditorActivity.this.mHexLineSeparator, NEWLINE);
+ return printable;
+ }
+ };
+ mReadTask.execute(this.mFso);
+ }
+
+ private void checkAndWrite() {
+ // Check that we have write access
+ try {
+ FileHelper.ensureWriteAccess(
+ ConsoleBuilder.getConsole(this),
+ this.mFso,
+ null);
+
+ // Write the file
+ ensureSyncWrite();
+
+ } catch (Exception ex) {
+ ExceptionUtil.translateException(
+ this, ex, false, true, new OnRelaunchCommandResult() {
+ @Override
+ public void onSuccess() {
+ // Write the file
+ ensureSyncWrite();
+ }
+
+ @Override
+ public void onFailed(Throwable cause) {/**NON BLOCK**/}
+
+ @Override
+ public void onCancelled() {/**NON BLOCK**/}
+ });
+ }
+ }
+
+ /**
+ * Method that checks that the write to disk operation was successfully and the
+ * expected bytes are written to disk.
+ * @hide
+ */
+ void ensureSyncWrite() {
+ try {
+ for (int i = 0; i < WRITE_RETRIES; i++) {
+ // Configure the writer
+ AsyncWriter writer = new AsyncWriter();
+
+ // Write to disk
+ final byte[] data = this.mEditor.getText().toString().getBytes();
+ long expected = data.length;
+ syncWrite(writer, data);
+
+ // Sleep a bit
+ Thread.sleep(150L);
+
+ // Is error?
+ if (writer.mCause != null) {
+ Log.e(TAG, "Write operation failed. Retries: " + i, writer.mCause);
+ if (i == (WRITE_RETRIES-1)) {
+ // Something was wrong. The file probably is corrupted
+ DialogHelper.showToast(
+ this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
+ break;
+ }
+
+ // Retry
+ continue;
+ }
+
+ // Check that all the bytes were written
+ FileSystemObject fso =
+ CommandHelper.getFileInfo(this, this.mFso.getFullPath(), true, null);
+ if (fso == null || fso.getSize() != expected) {
+ Log.e(TAG, String.format(
+ "Size is not the same. Expected: %d, Written: %d. Retries: %d",
+ expected, fso == null ? -1 : fso.getSize(), i));
+ if (i == (WRITE_RETRIES-1)) {
+ // Something was wrong. The destination data is not the same
+ // as the source data
+ DialogHelper.showToast(
+ this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
+ break;
+ }
+
+ // Retry
+ continue;
+ }
+
+ // Success. The file was saved
+ DialogHelper.showToast(
+ this, R.string.editor_successfully_saved, Toast.LENGTH_SHORT);
+ setDirty(false);
+
+ // Send a message that allow other activities to update his data
+ Intent intent = new Intent(FileManagerSettings.INTENT_FILE_CHANGED);
+ intent.putExtra(
+ FileManagerSettings.EXTRA_FILE_CHANGED_KEY, this.mFso.getFullPath());
+ sendBroadcast(intent);
+
+ // Done
+ break;
+
+ }
+ } catch (Exception ex) {
+ // Something was wrong, but the file was NOT written
+ Log.e(TAG, "The file wasn't written.", ex);
+ DialogHelper.showToast(
+ this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
+ }
+ }
+
+ /**
+ * Method that write the file.
+ *
+ * @param writer The command listener
+ * @param bytes The bytes to write
+ * @throws Exception If something was wrong
+ */
+ private void syncWrite(AsyncWriter writer, byte[] bytes) throws Exception {
+ // Create the writable command
+ WriteExecutable cmd =
+ CommandHelper.write(this, this.mFso.getFullPath(), writer, null);
+
+ // Obtain access to the buffer (IMP! don't close the buffer here, it's manage
+ // by the command)
+ OutputStream os = cmd.createOutputStream();
+ try {
+ // Retrieve the text from the editor
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ try {
+ // Buffered write
+ byte[] data = new byte[this.mBufferSize];
+ int read = 0, written = 0;
+ while ((read = bais.read(data, 0, this.mBufferSize)) != -1) {
+ os.write(data, 0, read);
+ written += read;
+ }
+ Log.i(TAG, "Bytes written: " + written); //$NON-NLS-1$
+ } finally {
+ try {
+ bais.close();
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+
+ } finally {
+ // Ok. Data is written or ensure buffer close
+ cmd.end();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void beforeTextChanged(
+ CharSequence s, int start, int count, int after) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ this.mEditStart = start;
+ this.mEditEnd = start + count;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void afterTextChanged(Editable s) {
+ setDirty(true);
+ if (this.mSyntaxHighlightProcessor != null) {
+ this.mSyntaxHighlightProcessor.process(s, this.mEditStart, this.mEditEnd);
+ }
+ }
+
+ /**
+ * Method that sets if the editor is dirty (has changed)
+ *
+ * @param dirty If the editor is dirty
+ * @hide
+ */
+ void setDirty(boolean dirty) {
+ this.mDirty = dirty;
+ this.invalidateOptionsMenu();
+ }
+
+ /**
+ * Check the dirty state of the editor, and ask the user to save the changes
+ * prior to exit.
+ */
+ public void checkDirtyState() {
+ if (this.mDirty) {
+ AlertDialog dlg = DialogHelper.createYesNoDialog(
+ this,
+ R.string.editor_dirty_ask_title,
+ R.string.editor_dirty_ask_msg,
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ dialog.dismiss();
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ }
+ });
+ DialogHelper.delegateDialogShow(this, dlg);
+ return;
+ }
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+
+ /**
+ * Method that check if a character is valid printable character
+ *
+ * @param c The character to check
+ * @return boolean If the character is printable
+ * @hide
+ */
+ static boolean isPrintableCharacter(char c) {
+ int cc = VALID_NON_PRINTABLE_CHARS.length;
+ for (int i = 0; i < cc; i++) {
+ if (c == VALID_NON_PRINTABLE_CHARS[i]) {
+ return true;
+ }
+ }
+ return TextUtils.isGraphic(c);
+ }
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, false);
+
+ theme.setBackgroundDrawable(this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$
+ View v = findViewById(R.id.editor);
+ theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
+ //- ProgressBar
+ Drawable dw = theme.getDrawable(this, "horizontal_progress_bar"); //$NON-NLS-1$
+ this.mProgressBar.setProgressDrawable(dw);
+ v = findViewById(R.id.editor_progress_msg);
+ theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
+
+ // Need a full process of syntax highlight
+ if (!this.mBinary && this.mSyntaxHighlight && this.mSyntaxHighlightProcessor != null) {
+ reloadSyntaxHighlight();
+ }
+ }
+
+ /**
+ * Method that applies the current theme to the hex viewer editor
+ * @hide
+ */
+ void applyHexViewerTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ TextView editor = (TextView)findViewById(R.id.editor);
+ editor.setTextAppearance(this, R.style.hexeditor_text_appearance);
+ editor.setTypeface(Typeface.MONOSPACE);
+ theme.setTextColor(this, editor, "text_color"); //$NON-NLS-1$
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/HistoryActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/HistoryActivity.java
new file mode 100644
index 000000000..ae534107c
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/HistoryActivity.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.activities;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.*;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.fragments.HistoryFragment;
+import me.toolify.backbone.model.History;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+
+/**
+ * An activity for show navigation history.
+ */
+public class HistoryActivity extends Activity {
+
+ private static final String TAG = "HistoryActivity"; //$NON-NLS-1$
+
+ private static boolean DEBUG = false;
+
+ HistoryFragment mHistoryFragment;
+
+ private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
+ applyTheme();
+ }
+ }
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCreate(Bundle state) {
+ if (DEBUG) {
+ Log.d(TAG, "HistoryActivity.onCreate"); //$NON-NLS-1$
+ }
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+ registerReceiver(this.mNotificationReceiver, filter);
+
+ //Set the main layout of the activity
+ setContentView(R.layout.history);
+
+ // Load the BoookmarksFragment
+ FragmentManager fm = getFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ mHistoryFragment = new HistoryFragment();
+ ft.add(R.id.fragment_content, mHistoryFragment);
+ ft.commit();
+
+ //Set in transition
+ overridePendingTransition(R.anim.translate_to_right_in, R.anim.hold_out);
+
+ //Initialize action bar
+ initTitleActionBar();
+
+ //Save state
+ super.onCreate(state);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "HistoryActivity.onDestroy"); //$NON-NLS-1$
+ }
+
+ // Unregister the receiver
+ try {
+ unregisterReceiver(this.mNotificationReceiver);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPause() {
+ //Set out transition
+ overridePendingTransition(R.anim.hold_in, R.anim.translate_to_left_out);
+ super.onPause();
+ }
+
+ /**
+ * Method that initializes the titlebar of the activity.
+ */
+ private void initTitleActionBar() {
+ //Configure the action bar options
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setTitle(R.string.history);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.history, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ back(true, null);
+ break;
+ case R.id.mnu_clear_history:
+ mHistoryFragment.clearHistory();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ back(true, null);
+ return true;
+ default:
+ return super.onKeyUp(keyCode, event);
+ }
+ }
+
+ /**
+ * Method that returns to previous activity and.
+ *
+ * @param cancelled Indicates if the activity was cancelled
+ * @param history The selected history
+ */
+ public void back(final boolean cancelled, final History history) {
+ Intent intent = new Intent();
+ if (cancelled) {
+ if (mHistoryFragment.mIsClearHistory) {
+ intent.putExtra(NavigationActivity.EXTRA_HISTORY_CLEAR, true);
+ }
+ setResult(RESULT_CANCELED, intent);
+ } else {
+ intent.putExtra(NavigationActivity.EXTRA_HISTORY_ENTRY_SELECTION, history);
+ setResult(RESULT_OK, intent);
+ }
+ finish();
+ }
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, false);
+
+ // -View
+ theme.setBackgroundDrawable(this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/NavigationActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/NavigationActivity.java
new file mode 100644
index 000000000..04d36662c
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/NavigationActivity.java
@@ -0,0 +1,1809 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.activities;
+
+import android.annotation.TargetApi;
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcEvent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.v4.widget.DrawerLayout;
+import android.util.Log;
+import android.view.InflateException;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.greenrobot.event.EventBus;
+import me.toolify.backbone.BuildConfig;
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.actionmode.PropertiesModeCallback;
+import me.toolify.backbone.activities.preferences.SettingsPreferences;
+import me.toolify.backbone.adapters.NavigationFragmentPagerAdapter;
+import me.toolify.backbone.bus.events.BookmarkDeleteEvent;
+import me.toolify.backbone.bus.events.BookmarkOpenEvent;
+import me.toolify.backbone.bus.events.BookmarkRefreshEvent;
+import me.toolify.backbone.bus.events.ClosePropertiesDrawerEvent;
+import me.toolify.backbone.bus.events.FilesystemStatusUpdateEvent;
+import me.toolify.backbone.bus.events.OpenPropertiesDrawerEvent;
+import me.toolify.backbone.console.Console;
+import me.toolify.backbone.console.ConsoleAllocException;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.fragments.HistoryFragment;
+import me.toolify.backbone.fragments.NavigationFragment;
+import me.toolify.backbone.fragments.NavigationFragment.OnNavigationRequestMenuListener;
+import me.toolify.backbone.listeners.OnCopyMoveListener;
+import me.toolify.backbone.listeners.OnRequestRefreshListener;
+import me.toolify.backbone.model.Bookmark;
+import me.toolify.backbone.model.DiskUsage;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.History;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.parcelables.NavigationViewInfoParcelable;
+import me.toolify.backbone.parcelables.SearchInfoParcelable;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.preferences.Bookmarks;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.NavigationLayoutMode;
+import me.toolify.backbone.preferences.NavigationSortMode;
+import me.toolify.backbone.preferences.ObjectIdentifier;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.dialogs.FilesystemInfoDialog;
+import me.toolify.backbone.ui.dialogs.FilesystemInfoDialog.OnMountListener;
+import me.toolify.backbone.ui.dialogs.InputNameDialog;
+import me.toolify.backbone.ui.policy.BookmarksActionPolicy;
+import me.toolify.backbone.ui.policy.CopyMoveActionPolicy;
+import me.toolify.backbone.ui.policy.CopyMoveActionPolicy.COPY_MOVE_OPERATION;
+import me.toolify.backbone.ui.policy.NewActionPolicy;
+import me.toolify.backbone.ui.widgets.BookmarksListView;
+import me.toolify.backbone.ui.widgets.Breadcrumb;
+import me.toolify.backbone.ui.widgets.BreadcrumbItem;
+import me.toolify.backbone.ui.widgets.BreadcrumbPager;
+import me.toolify.backbone.ui.widgets.BreadcrumbSpinner;
+import me.toolify.backbone.ui.widgets.FsoPropertiesView;
+import me.toolify.backbone.ui.widgets.NavigationCustomTitleView;
+import me.toolify.backbone.util.CommandHelper;
+import me.toolify.backbone.util.DialogHelper;
+import me.toolify.backbone.util.ExceptionUtil;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.StorageHelper;
+
+/**
+ * The main navigation activity. This activity is the center of the application.
+ * From this the user can navigate, search, make actions.
+ * This activity is singleTop, so when it is displayed no other activities exists in
+ * the stack.
+ * This cause an issue with the saved instance of this class, because if another activity
+ * is displayed, and the process is killed, NavigationActivity is started and the saved
+ * instance gets corrupted.
+ * For this reason the methods {link {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)} and
+ * {@link android.app.Activity#onRestoreInstanceState(android.os.Bundle)} are not implemented, and every time
+ * the app is killed, is restarted from his initial state.
+ */
+public class NavigationActivity extends AbstractNavigationActivity
+ implements OnRequestRefreshListener, OnCopyMoveListener,
+ OnNavigationRequestMenuListener, OnPageChangeListener {
+
+ private static final String TAG = "NavigationActivity"; //$NON-NLS-1$
+
+ private static boolean DEBUG = false;
+
+ /**
+ * Intent code for request a bookmark selection.
+ */
+ public static final int INTENT_REQUEST_BOOKMARK = 10001;
+
+ /**
+ * Intent code for request a history selection.
+ */
+ public static final int INTENT_REQUEST_HISTORY = 20001;
+
+ /**
+ * Intent code for request a search.
+ */
+ public static final int INTENT_REQUEST_SEARCH = 30001;
+
+
+ /**
+ * Constant for extra information about selected bookmark.
+ */
+ public static final String EXTRA_BOOKMARK_SELECTION =
+ "extra_bookmark_selection"; //$NON-NLS-1$
+
+ /**
+ * Constant for extra information about selected history entry.
+ */
+ public static final String EXTRA_HISTORY_ENTRY_SELECTION =
+ "extra_history_entry_selection"; //$NON-NLS-1$
+
+ /**
+ * Constant for extra information about clear selection action.
+ */
+ public static final String EXTRA_HISTORY_CLEAR =
+ "extra_history_clear_history"; //$NON-NLS-1$
+
+ /**
+ * Constant for extra information about selected search entry.
+ */
+ public static final String EXTRA_SEARCH_ENTRY_SELECTION =
+ "extra_search_entry_selection"; //$NON-NLS-1$
+
+ /**
+ * Constant for extra information about last search data.
+ */
+ public static final String EXTRA_SEARCH_LAST_SEARCH_DATA =
+ "extra_search_last_search_data"; //$NON-NLS-1$
+
+ /**
+ * Constant for extra information for request a navigation to the passed path.
+ */
+ public static final String EXTRA_NAVIGATE_TO =
+ "extra_navigate_to"; //$NON-NLS-1$
+
+ // The timeout needed to reset the exit status for back button
+ // After this time user need to tap 2 times the back button to
+ // exit, and the toast is shown again after the first tap.
+ private static final int RELEASE_EXIT_CHECK_TIMEOUT = 3500;
+
+ private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_SETTING_CHANGED) == 0) {
+ // The settings has changed
+ String key = intent.getStringExtra(FileManagerSettings.EXTRA_SETTING_CHANGED_KEY);
+ if (key != null) {
+ // Disk usage warning level
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_DISK_USAGE_WARNING_LEVEL.getId()) == 0) {
+
+ // Set the free disk space warning level of the breadcrumb widget
+ Breadcrumb breadcrumb = getCurrentNavigationFragment().getBreadcrumb();
+ String fds = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
+ (String)FileManagerSettings.
+ SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
+ breadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
+ breadcrumb.updateMountPointInfo();
+ return;
+ }
+
+ // Case sensitive sort
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_CASE_SENSITIVE_SORT.getId()) == 0) {
+ getCurrentNavigationFragment().refresh();
+ return;
+ }
+
+ // Display thumbs
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_DISPLAY_THUMBS.getId()) == 0) {
+ // Clean the icon cache applying the current theme
+ applyTheme();
+ return;
+ }
+
+ // Use flinger
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_USE_FLINGER.getId()) == 0) {
+ boolean useFlinger =
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_USE_FLINGER.getId(),
+ ((Boolean)FileManagerSettings.
+ SETTINGS_USE_FLINGER.
+ getDefaultValue()).booleanValue());
+ getCurrentNavigationFragment().setUseFlinger(useFlinger);
+ return;
+ }
+
+ // Access mode
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_ACCESS_MODE.getId()) == 0) {
+ // Is it necessary to create or exit of the ChRooted?
+ boolean chRooted =
+ FileManagerApplication.
+ getAccessMode().compareTo(AccessMode.SAFE) == 0;
+ if (chRooted != NavigationActivity.this.mChRooted) {
+ if (chRooted) {
+ createChRooted();
+ } else {
+ exitChRooted();
+ }
+ }
+ // Update bookmarks to reflect access mode change
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ }
+
+ // Filetime format mode
+ if (key.compareTo(FileManagerSettings.
+ SETTINGS_FILETIME_FORMAT_MODE.getId()) == 0) {
+ // Refresh the data
+ synchronized (FileHelper.DATETIME_SYNC) {
+ FileHelper.sReloadDateTimeFormats = true;
+ NavigationActivity.this.getCurrentNavigationFragment().refresh();
+ }
+ }
+ }
+
+ } else if (intent.getAction().compareTo(
+ FileManagerSettings.INTENT_FILE_CHANGED) == 0) {
+ // Retrieve the file that was changed
+ String file =
+ intent.getStringExtra(FileManagerSettings.EXTRA_FILE_CHANGED_KEY);
+ try {
+ FileSystemObject fso = CommandHelper.getFileInfo(context, file, null);
+ if (fso != null) {
+ getCurrentNavigationFragment().refresh(fso);
+ }
+ } catch (Exception e) {
+ ExceptionUtil.translateException(context, e, true, false);
+ }
+
+ } else if (intent.getAction().compareTo(
+ FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
+ applyTheme();
+
+ } else if (intent.getAction().compareTo(Intent.ACTION_TIME_CHANGED) == 0 ||
+ intent.getAction().compareTo(Intent.ACTION_DATE_CHANGED) == 0 ||
+ intent.getAction().compareTo(Intent.ACTION_TIMEZONE_CHANGED) == 0) {
+ // Refresh the data
+ synchronized (FileHelper.DATETIME_SYNC) {
+ FileHelper.sReloadDateTimeFormats = true;
+ NavigationActivity.this.getCurrentNavigationFragment().refresh();
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * @hide
+ */
+ private ActionBar mActionBar;
+ private Menu mOptionsMenu;
+ private MenuItem mFilesystemInfo;
+ private int mFilesystemStatus;
+ private DrawerLayout mDrawerLayout;
+ private ActionBarDrawerToggle mDrawerToggle;
+ private BookmarksListView mBookmarkDrawer;
+ private FsoPropertiesView mInfoDrawer;
+ private PropertiesModeCallback mPropertiesModeCallback;
+ private View mTitleLayout;
+ private NavigationCustomTitleView mTitle;
+ private Breadcrumb mBreadcrumb;
+
+ /**
+ * @hide
+ */
+ private BreadcrumbPager mBreadcrumbPager;
+ public NavigationFragmentPagerAdapter mPagerAdapter;
+ public ViewPager mViewPager;
+
+ private boolean mExitFlag = false;
+ private long mExitBackTimeout = -1;
+
+ public boolean mChRooted;
+
+ /**
+ * @hide
+ */
+ Handler mHandler;
+
+ /**
+ * @hide
+ */
+ private List mFilesForPaste;
+ /**
+ * @hide
+ */
+ private COPY_MOVE_OPERATION mPasteOperationType;
+
+ /**
+ * {@inheritDoc}
+ */
+ @TargetApi(16)
+ @Override
+ protected void onCreate(Bundle state) {
+
+ if (DEBUG) {
+ Log.d(TAG, "NavigationActivity.onCreate"); //$NON-NLS-1$
+ }
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
+ filter.addAction(FileManagerSettings.INTENT_FILE_CHANGED);
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+ filter.addAction(Intent.ACTION_DATE_CHANGED);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ registerReceiver(this.mNotificationReceiver, filter);
+
+ // Initialize NFC adapter
+ NfcAdapter mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter != null && Build.VERSION.SDK_INT > 16) {
+ mNfcAdapter.setBeamPushUrisCallback(new NfcAdapter.CreateBeamUrisCallback() {
+ @Override
+ public Uri[] createBeamUris(NfcEvent event) {
+ List selectedFiles =
+ getCurrentNavigationFragment().getSelectedFiles();
+ if (selectedFiles.size() > 0) {
+ List fileUri = new ArrayList();
+ for (FileSystemObject f : selectedFiles) {
+ //Beam ignores folders and system files
+ if (!FileHelper.isDirectory(f) && !FileHelper.isSystemFile(f)) {
+ fileUri.add(Uri.fromFile(new File(f.getFullPath())));
+ }
+ }
+ if (fileUri.size() > 0) {
+ return fileUri.toArray(new Uri[fileUri.size()]);
+ }
+ }
+ return null;
+ }
+ }, this);
+ }
+
+ setContentView(R.layout.navigation);
+
+ //Initialize activity console
+ init();
+
+ //Initialize viewPager after the action bar
+ initViewPager();
+
+ //Initialize action bar
+ mActionBar = getActionBar();
+ initTitleActionBar();
+
+ // Create ActionBar drawer toggle drawable
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
+ R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {
+
+ /** Called when a drawer has settled in a completely closed state. */
+ public void onDrawerClosed(View view) {
+ // Reload the fragments current dir into the breadcrumb
+ if (getCurrentBreadcrumb() != null) {
+ getCurrentBreadcrumb().changeBreadcrumbPath(getCurrentNavigationFragment().getCurrentDir(), mChRooted);
+ }
+ invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
+
+ // If the properties drawer was just closed, we should lock it closed and make sure
+ // that the properties action mode is closed too.
+ if (view instanceof FsoPropertiesView) {
+ EventBus.getDefault().post(new ClosePropertiesDrawerEvent());
+ }
+ }
+
+ /** Called when a drawer has settled in a completely open state. */
+ public void onDrawerOpened(View drawerView) {
+ getActionBar().setTitle(R.string.bookmarks);
+ invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
+ }
+ };
+
+ // Set the drawer toggle as the DrawerListener
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setHomeButtonEnabled(true);
+
+ mBookmarkDrawer = (BookmarksListView) findViewById(R.id.left_drawer);
+ mInfoDrawer = (FsoPropertiesView) findViewById(R.id.right_drawer);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mInfoDrawer);
+
+ // Apply the theme
+ applyTheme();
+
+ // Show welcome message
+ showWelcomeMsg();
+
+ //Save state
+ super.onCreate(state);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onNewIntent(Intent intent) {
+ //Initialize navigation
+ NavigationActivity.this.getCurrentNavigationFragment().initNavigation(true, intent);
+
+ //Check the intent action
+ checkIntent(intent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // Always unregister when an object no longer should be on the bus.
+ EventBus.getDefault().unregister(this);
+ EventBus.getDefault().unregister(mBookmarkDrawer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mBreadcrumbPager.setViewPager(mViewPager);
+ //mBreadcrumbPager.setOnPageChangeListener(this);
+
+// mSlidingStrip.setViewPager(mViewPager);
+// mSlidingStrip.setOnPageChangeListener(this);
+
+ // Register ourselves so that we can provide the initial value.
+ EventBus.getDefault().register(this);
+ EventBus.getDefault().register(mBookmarkDrawer);
+
+ // Tell the bookmarks fragment to refresh itself
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "NavigationActivity.onDestroy"); //$NON-NLS-1$
+ }
+
+ // Unregister the receiver
+ try {
+ unregisterReceiver(this.mNotificationReceiver);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ mBreadcrumbPager.refreshLayout();
+ }
+
+ /**
+ * Method that returns the current navigation view.
+ *
+ * @return NavigationFragment The current navigation view
+ */
+ public NavigationFragment getCurrentNavigationFragment() {
+ return mPagerAdapter.getFragment(mViewPager, mViewPager.getCurrentItem());
+ }
+
+
+ /**
+ * Method that returns the current breadcrumb view.
+ *
+ * @return BreadcrumbSpinner The current breadcrumb view
+ */
+ public BreadcrumbSpinner getCurrentBreadcrumb() {
+ return mBreadcrumbPager.getBreadcrumb(mViewPager.getCurrentItem());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void pairBreadcrumb(int position, NavigationFragment fragment) {
+ try {
+ Breadcrumb b = mBreadcrumbPager.getBreadcrumb(position);
+ if (b != null) {
+ // Breadcrumb has already been loaded into the breadcrumb pager
+ fragment.setBreadcrumb(mBreadcrumbPager.getBreadcrumb(position));
+ } else {
+ // Otherwise it hasn't yet, so lets queue it up
+ mBreadcrumbPager.queuePairFragment(position, fragment);
+ }
+ } catch (Throwable ex) {
+ Log.e(TAG,
+ String.format("Failed to pair breadcrumb %d", //$NON-NLS-1$
+ position, ex));
+ }
+ }
+
+ /**
+ * Method that initializes the activity.
+ */
+ private void init() {
+ this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;
+
+ this.mHandler = new Handler();
+ this.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Create the default console (from the preferences)
+ try {
+ Console console = ConsoleBuilder.getConsole(NavigationActivity.this);
+ if (console == null) {
+ throw new ConsoleAllocException("console == null"); //$NON-NLS-1$
+ }
+ } catch (Throwable ex) {
+ if (!NavigationActivity.this.isChRooted()) {
+ //Show exception and exists
+ Log.e(TAG, getString(R.string.msgs_cant_create_console), ex);
+ // We don't have any console
+ // Show exception and exists
+ DialogHelper.showToast(
+ NavigationActivity.this,
+ R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
+ NavigationActivity.this.exit();
+ return;
+ }
+
+ // We are in a trouble (something is not allowing creating the console)
+ // Ask the user to return to prompt or root access mode mode with a
+ // non-privileged console, prior to make crash the application
+ NavigationActivity.this.askOrExit();
+ return;
+ }
+ }
+ });
+ }
+
+ /**
+ * Method that displays a welcome message the first time the user
+ * access the application
+ */
+ private void showWelcomeMsg() {
+ boolean firstUse = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_FIRST_USE.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_FIRST_USE.getDefaultValue()).booleanValue());
+
+ //Display the welcome message?
+ if (firstUse) {
+ AlertDialog dialog = DialogHelper.createAlertDialog(
+ this, R.drawable.ic_launcher,
+ R.string.welcome_title, getString(R.string.welcome_msg), false);
+ DialogHelper.delegateDialogShow(this, dialog);
+
+ // Don't display again this dialog
+ try {
+ Preferences.savePreference(
+ FileManagerSettings.SETTINGS_FIRST_USE, Boolean.FALSE, true);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * Method that initializes the ViewPager and NavigationPagerAdapter
+ */
+ private void initViewPager(){
+ mViewPager = (ViewPager)findViewById(R.id.navigation_pager);
+
+ // Plug the ViewPager into the Pager Adapter and set the number of pages
+ mPagerAdapter = new NavigationFragmentPagerAdapter(this, getFragmentManager());
+ mViewPager.setAdapter(mPagerAdapter);
+ mViewPager.setHorizontalScrollBarEnabled(true);
+
+ // TODO this is a pretty much a hack job for now, and proper state
+ // retaining needs to be implemented for tablets
+ mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount());
+ }
+
+ /**
+ * Method that initializes the titlebar of the activity.
+ */
+ private void initTitleActionBar() {
+ //Inflate the view and associate breadcrumb
+ mTitleLayout = getLayoutInflater().inflate(
+ R.layout.navigation_view_customtitle, null, false);
+ mTitle = (NavigationCustomTitleView)mTitleLayout.findViewById(R.id.navigation_title_flipper);
+ mBreadcrumb = (Breadcrumb)mTitle.findViewById(R.id.breadcrumb_view);
+ //mSlidingStrip = (PagerSlidingTabStrip)mTitleLayout.findViewById(R.id.breadcrumb_sliding_strip);
+ mBreadcrumbPager = (BreadcrumbPager)mTitleLayout.findViewById(R.id.breadcrumb_pager);
+
+ // Set the free disk space warning level of the breadcrumb widget
+ String fds = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
+ (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
+ mBreadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
+ //Configure the action bar options
+ mActionBar.setDisplayOptions(
+ ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
+ mActionBar.setCustomView(mTitleLayout);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void updateTitleActionBar() {
+
+ NavigationFragment navigationFragment = getCurrentNavigationFragment();
+ mTitle.setOnHistoryListener(navigationFragment);
+ navigationFragment.setOnHistoryListener(navigationFragment);
+ navigationFragment.setOnNavigationOnRequestMenuListener(this);
+ }
+
+ /**
+ * Method that verifies the intent passed to the activity, and checks
+ * if a request is made like Search.
+ *
+ * @param intent The intent to check
+ * @hide
+ */
+ void checkIntent(Intent intent) {
+ //Search action
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ Intent searchIntent = new Intent(this, SearchActivity.class);
+ searchIntent.setAction(Intent.ACTION_SEARCH);
+ //- SearchActivity.EXTRA_SEARCH_DIRECTORY
+ searchIntent.putExtra(
+ SearchActivity.EXTRA_SEARCH_DIRECTORY,
+ getCurrentNavigationFragment().getCurrentDir());
+ //- SearchManager.APP_DATA
+ if (intent.getBundleExtra(SearchManager.APP_DATA) != null) {
+ Bundle bundle = new Bundle();
+ bundle.putAll(intent.getBundleExtra(SearchManager.APP_DATA));
+ searchIntent.putExtra(SearchManager.APP_DATA, bundle);
+ }
+ //-- SearchManager.QUERY
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ if (query != null) {
+ searchIntent.putExtra(SearchManager.QUERY, query);
+ }
+ //- android.speech.RecognizerIntent.EXTRA_RESULTS
+ ArrayList extraResults =
+ intent.getStringArrayListExtra(android.speech.RecognizerIntent.EXTRA_RESULTS);
+ if (extraResults != null) {
+ searchIntent.putStringArrayListExtra(
+ android.speech.RecognizerIntent.EXTRA_RESULTS, extraResults);
+ }
+ startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
+ return;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (checkBackAction()) {
+ return true;
+ }
+
+ // An exit event has occurred, force the destroy the consoles
+ exit();
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ this.mOptionsMenu = menu;
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.navigation, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+
+ // Ensure that the appropriate sort mode menuItem radio is selected
+ NavigationSortMode sortMode = NavigationSortMode.fromId(Preferences.getSharedPreferences()
+ .getInt(FileManagerSettings.SETTINGS_SORT_MODE.getId(),
+ ((ObjectIdentifier)FileManagerSettings.SETTINGS_SORT_MODE
+ .getDefaultValue()).getId()));
+ switch(sortMode) {
+ case DATE_ASC:
+ menu.findItem(R.id.mnu_actions_sort_by_date_asc).setChecked(true);
+ break;
+ case DATE_DESC:
+ menu.findItem(R.id.mnu_actions_sort_by_date_desc).setChecked(true);
+ break;
+ case NAME_ASC:
+ menu.findItem(R.id.mnu_actions_sort_by_name_asc).setChecked(true);
+ break;
+ case NAME_DESC:
+ menu.findItem(R.id.mnu_actions_sort_by_name_desc).setChecked(true);
+ break;
+ }
+
+ // Ensure that the appropriate layout mode menuItem radio is selected
+ NavigationLayoutMode layoutMode = NavigationLayoutMode.fromId(Preferences.
+ getSharedPreferences().getInt(FileManagerSettings.SETTINGS_LAYOUT_MODE.getId(),
+ ((ObjectIdentifier)FileManagerSettings.SETTINGS_LAYOUT_MODE
+ .getDefaultValue()).getId()));
+ switch(layoutMode) {
+ case ICONS:
+ menu.findItem(R.id.mnu_actions_layout_icons).setChecked(true);
+ break;
+ case DETAILS:
+ menu.findItem(R.id.mnu_actions_layout_details).setChecked(true);
+ break;
+ case SIMPLE:
+ menu.findItem(R.id.mnu_actions_layout_simple).setChecked(true);
+ break;
+ }
+
+ // Ensure that the other view mode options are correctly selected
+ int[] menuSettingIDs = new int[] {
+ R.id.mnu_actions_show_dirs_first,
+ R.id.mnu_actions_show_hidden,
+ R.id.mnu_actions_show_system,
+ R.id.mnu_actions_show_symlinks
+ };
+ FileManagerSettings[] settingShows = new FileManagerSettings[] {
+ FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST,
+ FileManagerSettings.SETTINGS_SHOW_HIDDEN,
+ FileManagerSettings.SETTINGS_SHOW_SYSTEM,
+ FileManagerSettings.SETTINGS_SHOW_SYMLINKS
+ };
+ for(int i = 0; i < menuSettingIDs.length; i++) {
+ MenuItem item = menu.findItem(menuSettingIDs[i]);
+ if(item != null) {
+ item.setChecked(Preferences.getSharedPreferences().getBoolean(
+ settingShows[i].getId(),
+ Boolean.parseBoolean(settingShows[i].getDefaultValue().toString())));
+ }
+ }
+
+ // Make paste action visible if there are files available for pasting
+ menu.findItem(R.id.mnu_actions_paste_selection).setVisible(this.onAreFilesMarkedForPaste());
+ // Toggle layout mode visibility so that it is only visible in development mode
+ menu.findItem(R.id.mnu_actions_layout_mode).setVisible(BuildConfig.DEBUG);
+ mFilesystemInfo = menu.findItem(R.id.mnu_actions_show_filesystem_info);
+ setFilesystemStatusDrawable(mFilesystemStatus);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+
+ /**
+ * This method is called by various pieces of code responsible for updating file listing or
+ * breadcrumb data. The method receives all {@link me.toolify.backbone.bus.events.FilesystemStatusUpdateEvent}
+ * events generated by the various asyncTasks responsible for gathering and sending file info.
+ *
+ * @param event an event containing the filesystem status code
+ */
+ public void onEvent(FilesystemStatusUpdateEvent event) {
+ mFilesystemStatus = event.status;
+ setFilesystemStatusDrawable(event.status);
+ }
+
+ private void setFilesystemStatusDrawable(int fileSystemstatus){
+
+ TypedArray a = getTheme().obtainStyledAttributes(R.styleable.FileManager);
+
+ switch (fileSystemstatus) {
+
+ case FilesystemStatusUpdateEvent.INDICATOR_UNLOCKED:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setIcon(getResources().
+ getDrawable(a.getResourceId(R.styleable.FileManager_actionIconLockOpen,
+ R.drawable.ic_action_holo_dark_lock_open)));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_LOCKED:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setIcon(getResources().
+ getDrawable(a.getResourceId(R.styleable.FileManager_actionIconLockClosed,
+ R.drawable.ic_action_holo_dark_lock_closed)));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_WARNING:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setIcon(getResources().
+ getDrawable(a.getResourceId(R.styleable.FileManager_actionIconWarning,
+ R.drawable.ic_action_holo_dark_warning)));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_REFRESHING:
+ if (mFilesystemInfo != null) {
+ setFilesystemInfoProgressState(true);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_STOP_REFRESHING:
+ if (mFilesystemInfo != null) {
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * This function switches an action item between its normal icon and an indeterminate progress
+ * circle
+ * @param refreshing value is true if the action item should show the progress bar
+ */
+ public void setFilesystemInfoProgressState(final boolean refreshing) {
+ if (mFilesystemInfo != null) {
+ final MenuItem refreshItem = mOptionsMenu
+ .findItem(R.id.mnu_actions_show_filesystem_info);
+ if (refreshItem != null) {
+ if (refreshing) {
+ refreshItem.setActionView(R.layout.actionbar_indeterminate_progress);
+ } else {
+ refreshItem.setActionView(null);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is called by the bookmarks list when a bookmark is selected for opening. The
+ * method receives all {@link me.toolify.backbone.bus.events.BookmarkOpenEvent}
+ * events and opens the referenced directory in the currently visible
+ * {@link me.toolify.backbone.fragments.NavigationFragment}.
+ *
+ * @param event an event containing a bookmarked directory that should be opened
+ */
+ public void onEvent(BookmarkOpenEvent event) {
+ String path = event.path;
+ Bookmark b = Bookmarks.getBookmark(getContentResolver(), path);
+ // Check that the bookmark exists
+ try {
+ FileSystemObject fso = CommandHelper.getFileInfo(this, path, null);
+ if (fso != null) {
+ getCurrentNavigationFragment().open(fso);
+ mDrawerLayout.closeDrawer(mBookmarkDrawer);
+ } else {
+ // The bookmark not exists, delete the user-defined bookmark
+ try {
+ if(b.getType() == Bookmark.BOOKMARK_TYPE.USER_DEFINED)
+ {
+ EventBus.getDefault().post(new BookmarkDeleteEvent(path));
+ Bookmarks.removeBookmark(this, b);
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ }
+ } catch (Exception e) {
+ // Capture the exception
+ ExceptionUtil.translateException(this, e);
+ if (e instanceof NoSuchFileOrDirectory || e instanceof FileNotFoundException) {
+ // The bookmark not exists, delete the user-defined bookmark
+ try {
+ if(b.getType() == Bookmark.BOOKMARK_TYPE.USER_DEFINED)
+ EventBus.getDefault().post(new BookmarkDeleteEvent(path));
+ } catch (Exception ex) {/**NON BLOCK**/}
+ }
+ return;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+
+ // Drawer Toggle pass-off
+ if (mDrawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ }
+
+ // Action Items
+ switch (item.getItemId()) {
+ case R.id.mnu_actions_sort_by_date_asc:
+ FileManagerSettings.setSorting(NavigationSortMode.DATE_ASC);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_sort_by_date_desc:
+ FileManagerSettings.setSorting(NavigationSortMode.DATE_DESC);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_sort_by_name_asc:
+ FileManagerSettings.setSorting(NavigationSortMode.NAME_ASC);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_sort_by_name_desc:
+ FileManagerSettings.setSorting(NavigationSortMode.NAME_DESC);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_show_dirs_first:
+ FileManagerSettings.toggleViewPreference(FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_show_hidden:
+ FileManagerSettings.toggleViewPreference(FileManagerSettings.SETTINGS_SHOW_HIDDEN);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_show_system:
+ FileManagerSettings.toggleViewPreference(FileManagerSettings.SETTINGS_SHOW_SYSTEM);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_show_symlinks:
+ FileManagerSettings.toggleViewPreference(FileManagerSettings.SETTINGS_SHOW_SYMLINKS);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_layout_simple:
+ FileManagerSettings.setLayout(NavigationLayoutMode.SIMPLE);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_layout_details:
+ FileManagerSettings.setLayout(NavigationLayoutMode.DETAILS);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ case R.id.mnu_actions_layout_icons:
+ FileManagerSettings.setLayout(NavigationLayoutMode.ICONS);
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ // Show information of the filesystem
+ case R.id.mnu_actions_show_filesystem_info:
+ MountPoint mp = getCurrentNavigationFragment().getBreadcrumb().getMountPointInfo();
+ DiskUsage du = getCurrentNavigationFragment().getBreadcrumb().getDiskUsageInfo();
+ showMountPointInfo(mp, du);
+ break;
+
+ case R.id.mnu_actions_history:
+ openHistory();
+ break;
+
+ case R.id.mnu_actions_search:
+ openSearch();
+ break;
+
+ // Refresh the current navigation fragment
+ case R.id.mnu_actions_refresh:
+ getCurrentNavigationFragment().refresh();
+ break;
+
+ // Create new object
+ case R.id.mnu_actions_new_file:
+ showFileTypeDialog(new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which >= 0)
+ showInputNameDialog(which);
+ else dialog.dismiss();
+ }
+ });
+ break;
+
+ // Paste selection
+ case R.id.mnu_actions_paste_selection:
+ if (true) {
+ CopyMoveActionPolicy.triggerCopyMoveFileSystemObjects(
+ NavigationActivity.this,
+ this.onRequestFilesMarkedForPaste(),
+ this.onRequestPasteOperationType(),
+ getCurrentNavigationFragment(),
+ getCurrentNavigationFragment(),
+ NavigationActivity.this);
+ /* Clear files as soon as the user decides to paste, thus permitting more copy
+ selections while this paste task finishes. */
+ onClearFilesMarkedForPaste();
+ }
+ break;
+
+ // Open a properties drawer on the current directory
+ case R.id.mnu_actions_properties_current_folder:
+ EventBus.getDefault().post(new OpenPropertiesDrawerEvent(
+ getCurrentNavigationFragment().getCurrentDir()));
+ break;
+
+ // Add current directory to bookmarks
+ case R.id.mnu_actions_add_to_bookmarks:
+ try {
+ FileSystemObject bookmarkFso = CommandHelper.getFileInfo(this,
+ getCurrentNavigationFragment().getCurrentDir(),
+ null);
+ BookmarksActionPolicy.addToBookmarks(this, bookmarkFso);
+ EventBus.getDefault().post(new BookmarkRefreshEvent());
+ } catch (Exception e) {
+ ExceptionUtil.translateException(this, e, true, false);
+ }
+ break;
+ // Open settings
+ case R.id.mnu_settings:
+ Intent settings = new Intent(
+ NavigationActivity.this, SettingsPreferences.class);
+ startActivity(settings);
+ break;
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (data != null) {
+ switch (requestCode) {
+ case INTENT_REQUEST_HISTORY:
+ if (resultCode == RESULT_OK) {
+ //Change current directory
+ History history =
+ (History)data.getSerializableExtra(EXTRA_HISTORY_ENTRY_SELECTION);
+ navigateToHistory(history);
+ } else if (resultCode == RESULT_CANCELED) {
+ boolean clear = data.getBooleanExtra(EXTRA_HISTORY_CLEAR, false);
+ if (clear) {
+ clearHistory();
+ }
+ }
+ break;
+
+ case INTENT_REQUEST_SEARCH:
+ if (resultCode == RESULT_OK) {
+ //Change directory?
+ FileSystemObject fso =
+ (FileSystemObject)data.
+ getSerializableExtra(EXTRA_SEARCH_ENTRY_SELECTION);
+ SearchInfoParcelable searchInfo =
+ data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
+ if (fso != null) {
+ //Goto to new directory
+ getCurrentNavigationFragment().open(fso, searchInfo);
+ }
+ } else if (resultCode == RESULT_CANCELED) {
+ SearchInfoParcelable searchInfo =
+ data.getParcelableExtra(EXTRA_SEARCH_LAST_SEARCH_DATA);
+ if (searchInfo != null && searchInfo.isSuccessNavigation()) {
+ //Navigate to previous history
+ back();
+ } else {
+ // I don't know is the search view was changed, so try to do a refresh
+ // of the navigation view
+ getCurrentNavigationFragment().refresh(true);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRequestRefresh(Object o, boolean clearSelection) {
+ if (o instanceof FileSystemObject) {
+ // Refresh only the item
+ this.getCurrentNavigationFragment().refresh((FileSystemObject) o);
+ } else if (o == null) {
+ // Refresh all
+ getCurrentNavigationFragment().refresh();
+ }
+ if (clearSelection) {
+ this.getCurrentNavigationFragment().onDeselectAll();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRequestRemove(Object o, boolean clearSelection) {
+ if (o instanceof FileSystemObject) {
+ // Remove from view
+ this.getCurrentNavigationFragment().removeItem((FileSystemObject) o);
+
+ //Remove from history
+ getCurrentNavigationFragment().removeFromHistory((FileSystemObject) o);
+ } else {
+ onRequestRefresh(null, clearSelection);
+ }
+ if (clearSelection) {
+ this.getCurrentNavigationFragment().onDeselectAll();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onBreadcrumbItemClick(File item) {
+ getCurrentNavigationFragment().changeCurrentDir(item.getAbsolutePath());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onNavigateTo(Object o) {
+ // Ignored
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRequestMenu(NavigationFragment navFragment, FileSystemObject item) {
+
+ }
+
+ /**
+ * Show/hide the "selection" action mode, according to the number of
+ * selected messages and the visibility of the fragment. Also update the
+ * content (title and menus) if necessary.
+ */
+ public void startPropertiesActionMode(FileSystemObject fso) {
+ mPropertiesModeCallback = new PropertiesModeCallback(this, fso);
+ mPropertiesModeCallback.setOnRequestRefreshListener(this);
+ mPropertiesModeCallback.setOnCopyMoveListener(this);
+ startActionMode(mPropertiesModeCallback);
+ }
+
+ /**
+ * Finish the "properties" action mode.
+ *
+ */
+ private void finishPropertiesActionMode() {
+ // Close the action mode
+ if (isInPropertiesActionMode()) {
+ mPropertiesModeCallback.setClosedByUser(false);
+ mPropertiesModeCallback.finish();
+ }
+ // Close and lock the info drawer
+ if (mDrawerLayout.isDrawerOpen(mInfoDrawer)) {
+ mDrawerLayout.closeDrawer(mInfoDrawer);
+ }
+ // The properties drawer has been closed, so lock it shut and unlock the bookmarks bar.
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mInfoDrawer);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mBookmarkDrawer);
+ }
+
+ /**
+ * @return true if the list is in the "selection" mode.
+ */
+ private boolean isInPropertiesActionMode() {
+ if (mPropertiesModeCallback == null) {
+ return false;
+ } else if (mPropertiesModeCallback.inPropertiesActionMode()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Method that show the information of a filesystem mount point.
+ *
+ * @param mp The mount point info
+ * @param du The disk usage of the mount point
+ */
+ private void showMountPointInfo(MountPoint mp, DiskUsage du) {
+ //Has mount point info?
+ if (mp == null) {
+ //There is no information
+ AlertDialog alert =
+ DialogHelper.createWarningDialog(
+ this,
+ R.string.filesystem_info_warning_title,
+ R.string.filesystem_info_warning_msg);
+ DialogHelper.delegateDialogShow(this, alert);
+ return;
+ }
+
+ //Show a the filesystem info dialog
+ FilesystemInfoDialog dialog = new FilesystemInfoDialog(this, mp, du);
+ dialog.setOnMountListener(new OnMountListener() {
+ @Override
+ public void onRemount(MountPoint mountPoint) {
+ //Update the statistics of breadcrumb, only if mount point is the same
+ Breadcrumb breadcrumb = getCurrentNavigationFragment().getBreadcrumb();
+ if (breadcrumb.getMountPointInfo().compareTo(mountPoint) == 0) {
+ breadcrumb.updateMountPointInfo();
+ }
+ }
+ });
+ dialog.show();
+ }
+
+ /**
+ * Method that checks the action that must be realized when the
+ * back button is pushed.
+ *
+ * @return boolean Indicates if the action must be intercepted
+ */
+ private boolean checkBackAction() {
+ // We need a basic structure to check this
+ if (getCurrentNavigationFragment() == null) return false;
+
+ //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 ||
+ (now - this.mExitBackTimeout) > RELEASE_EXIT_CHECK_TIMEOUT);
+
+ //Check if there no history and if the user was advised in the last back action
+ if (this.mExitFlag && (this.mExitFlag != flag || timeout)) {
+ //Communicate the user that the next time the application will be closed
+ this.mExitBackTimeout = System.currentTimeMillis();
+ DialogHelper.showToast(this, R.string.msgs_push_again_to_exit, Toast.LENGTH_SHORT);
+ return true;
+ }
+
+ //Back action not applied
+ return !this.mExitFlag;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onSearchRequested() {
+ Bundle bundle = new Bundle();
+ bundle.putString(
+ SearchActivity.EXTRA_SEARCH_DIRECTORY,
+ getCurrentNavigationFragment().getCurrentDir());
+ startSearch(Preferences.getLastSearch(), true, bundle, false);
+ return true;
+ }
+
+ /**
+ * Method that clears the fragment history.
+ */
+ private void clearHistory() {
+ getCurrentNavigationFragment().mHistory.clear();
+ getCurrentNavigationFragment().onCheckHistory();
+ }
+
+ /**
+ * Method that navigates to the passed history reference.
+ *
+ * @param history The history reference
+ * @return boolean A problem occurs while navigate
+ */
+ public synchronized boolean navigateToHistory(History history) {
+ try {
+ NavigationFragment currentNavFragment = getCurrentNavigationFragment();
+ //Gets the history
+ History realHistory = currentNavFragment.mHistory.get(history.getPosition());
+
+ //Navigate to item. Check what kind of history is
+ if (realHistory.getItem() instanceof NavigationViewInfoParcelable) {
+ //Navigation
+ NavigationViewInfoParcelable info =
+ (NavigationViewInfoParcelable)realHistory.getItem();
+ // Selected items must not be restored from on history navigation
+ info.setSelectedFiles(currentNavFragment.getSelectedFiles());
+ if (!currentNavFragment.onRestoreState(info)) {
+ return true;
+ }
+
+ } else if (realHistory.getItem() instanceof SearchInfoParcelable) {
+ //Search (open search with the search results)
+ SearchInfoParcelable info = (SearchInfoParcelable)realHistory.getItem();
+ Intent searchIntent = new Intent(this, SearchActivity.class);
+ searchIntent.setAction(SearchActivity.ACTION_RESTORE);
+ searchIntent.putExtra(SearchActivity.EXTRA_SEARCH_RESTORE, (Parcelable)info);
+ startActivityForResult(searchIntent, INTENT_REQUEST_SEARCH);
+ } else {
+ //The type is unknown
+ throw new IllegalArgumentException("Unknown history type"); //$NON-NLS-1$
+ }
+
+ //Remove the old history
+ int cc = realHistory.getPosition();
+ for (int i = currentNavFragment.mHistory.size() - 1; i >= cc; i--) {
+ currentNavFragment.mHistory.remove(i);
+ }
+
+ //Navigate
+ return true;
+
+ } catch (Throwable ex) {
+ if (history != null) {
+ Log.e(TAG,
+ String.format("Failed to navigate to history %d: %s", //$NON-NLS-1$
+ Integer.valueOf(history.getPosition()),
+ history.getItem().getTitle()), ex);
+ } else {
+ Log.e(TAG,
+ String.format("Failed to navigate to history: null", ex)); //$NON-NLS-1$
+ }
+ this.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ DialogHelper.showToast(
+ NavigationActivity.this,
+ R.string.msgs_history_unknown, Toast.LENGTH_LONG);
+ }
+ });
+
+ //Not change directory
+ return false;
+ }
+ }
+
+ /**
+ * Method that request a back action over the navigation history.
+ *
+ * @return boolean If a back action was applied
+ */
+ public boolean back() {
+ NavigationFragment currentNavFragment = getCurrentNavigationFragment();
+ // Check that has valid history
+ while (currentNavFragment.mHistory.size() > 0) {
+ History h = currentNavFragment.mHistory.get(currentNavFragment.mHistory.size() - 1);
+ if (h.getItem() instanceof NavigationViewInfoParcelable) {
+ // Verify that the path exists
+ String path = ((NavigationViewInfoParcelable)h.getItem()).getCurrentDir();
+
+ try {
+ FileSystemObject info = CommandHelper.getFileInfo(this, path, null);
+ if (info != null) {
+ break;
+ }
+ currentNavFragment.mHistory.remove(currentNavFragment.mHistory.size() - 1);
+ } catch (Exception e) {
+ ExceptionUtil.translateException(this, e, true, false);
+ currentNavFragment.mHistory.remove(currentNavFragment.mHistory.size() - 1);
+ }
+ } else {
+ break;
+ }
+ }
+
+ //Extract a history from the
+ if (currentNavFragment.mHistory.size() > 0) {
+ //Navigate to history
+ return navigateToHistory(currentNavFragment.mHistory.get(currentNavFragment.mHistory.size() - 1));
+ }
+
+ //Nothing to apply
+ return false;
+ }
+
+ /**
+ * Method that show a new dialog for input a name.
+ *
+ * @param fileTypeIndex The file_type array index associated
+ */
+ private void showInputNameDialog(final int fileTypeIndex) {
+ String[] fileTypes = getResources().getStringArray(R.array.file_types);
+ String title = fileTypes[0];
+ if(fileTypeIndex < fileTypes.length)
+ title = fileTypes[fileTypeIndex];
+
+ //Show the input name dialog
+ final InputNameDialog inputNameDialog =
+ new InputNameDialog(
+ this,
+ getCurrentNavigationFragment().onRequestCurrentItems(),
+ title);
+ inputNameDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ //Retrieve the name an execute the action
+ try {
+ String name = inputNameDialog.getName();
+ createNewFileSystemObject(fileTypeIndex, name);
+ } catch (InflateException e) {
+ Log.e(TAG, "Could not create the input name dialog");
+ }
+ }
+ });
+ inputNameDialog.show();
+ }
+
+ private static int getFileTypeIcon(Context context, int position)
+ {
+ TypedArray ar = context.getResources().obtainTypedArray(R.array.file_type_icons);
+ if(position < 0 || position > ar.length()) return 0;
+ return ar.getResourceId(position, 0);
+ }
+
+ private void showFileTypeDialog(final DialogInterface.OnClickListener onclick)
+ {
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_list_item_1,
+ getResources().getStringArray(R.array.file_types)){
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View ret = super.getView(position, convertView, parent);
+ if(ret instanceof TextView)
+ {
+ int res = getFileTypeIcon(getContext(), position);
+ if(res > 0)
+ {
+ Drawable d = getResources().getDrawable(res);
+ ((TextView)ret).setCompoundDrawablePadding(8);
+ ((TextView)ret).setCompoundDrawablesRelativeWithIntrinsicBounds(
+ d, null, null, null);
+ }
+ return ret;
+ }
+ return ret;
+ }
+ };
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.pick_file_type)
+ .setAdapter(adapter, onclick)
+ .setNegativeButton(R.string.cancel, onclick)
+ .create()
+ .show();
+ }
+
+ /**
+ * Method that create the a new file system object.
+ *
+ * @param fileTypeIndex The file_type array index
+ * @param name The name of the file system object
+ * @hide
+ */
+ void createNewFileSystemObject(final int fileTypeIndex, final String name) {
+ switch (fileTypeIndex) {
+ case 0:
+ NewActionPolicy.createNewDirectory(this, name, getCurrentNavigationFragment(), this);
+ break;
+ case 1:
+ NewActionPolicy.createNewFile(this, name, getCurrentNavigationFragment(), this);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * This method takes a file, opens a right-hand drawerLayout, populates the drawer with the
+ * file's information, and starts an associated {@link me.toolify.backbone.actionmode.PropertiesModeCallback}
+ *
+ * @param event an event referencing the file to provide information for.
+ */
+ public void onEvent(OpenPropertiesDrawerEvent event) {
+
+
+ // Resolve the full path
+ String path = String.valueOf(event.item);
+ if (event.item instanceof FileSystemObject) {
+ path = ((FileSystemObject)event.item).getFullPath();
+ }
+
+ // Prior to show the dialog, refresh the item reference
+ FileSystemObject fso = null;
+ try {
+ fso = CommandHelper.getFileInfo(this, path, false, null);
+ if (fso == null) {
+ throw new NoSuchFileOrDirectory(path);
+ }
+
+ } catch (Exception e) {
+ // Notify the user
+ ExceptionUtil.translateException(this, e);
+
+ // Remove the object
+ if (e instanceof FileNotFoundException || e instanceof NoSuchFileOrDirectory) {
+ // If have a FileSystemObject reference then there is no need to search
+ // the path (less resources used)
+ if (event.item instanceof FileSystemObject) {
+ getCurrentNavigationFragment().removeItem((FileSystemObject) event.item);
+ } else {
+ getCurrentNavigationFragment().removeItem((String) event.item);
+ }
+ }
+ return;
+ }
+
+ if (mDrawerLayout.isDrawerOpen(mInfoDrawer)) {
+ mDrawerLayout.closeDrawer(mInfoDrawer);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mInfoDrawer);
+ finishPropertiesActionMode();
+ } else {
+ startPropertiesActionMode(fso);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mInfoDrawer);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mBookmarkDrawer);
+ mDrawerLayout.openDrawer(mInfoDrawer);
+ mInfoDrawer.loadFso(fso);
+ }
+ }
+
+ /**
+ * This method closes an open properties drawer and calls finish() on the associated
+ * {@link me.toolify.backbone.actionmode.PropertiesModeCallback} action mode.
+ *
+ * @param event a trigger event that contains no additional information.
+ */
+ public void onEvent(ClosePropertiesDrawerEvent event) {
+ finishPropertiesActionMode();
+ }
+
+ /**
+ * Method that opens the history activity.
+ * @hide
+ */
+ void openHistory() {
+ Intent historyIntent = new Intent(this, HistoryActivity.class);
+ historyIntent.putExtra(HistoryFragment.EXTRA_HISTORY_LIST, (Serializable)this.getCurrentNavigationFragment().mHistory);
+ historyIntent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ startActivityForResult(historyIntent, INTENT_REQUEST_HISTORY);
+ }
+
+ /**
+ * Method that opens the search activity.
+ * @hide
+ */
+ void openSearch() {
+ onSearchRequested();
+ }
+
+ /**
+ * Method that ask the user to change the access mode prior to crash.
+ * @hide
+ */
+ public void askOrExit() {
+ //Show a dialog asking the user
+ AlertDialog dialog =
+ DialogHelper.createYesNoDialog(
+ this,
+ R.string.msgs_change_to_prompt_access_mode_title,
+ R.string.msgs_change_to_prompt_access_mode_msg,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface alertDialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ // We don't have any console
+ // Show exception and exit
+ DialogHelper.showToast(
+ NavigationActivity.this,
+ R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
+ exit();
+ return;
+ }
+
+ // Ok. Now try to change to prompt mode. Any crash
+ // here is a fatal error. We won't have any console to operate.
+ try {
+ // Change console
+ ConsoleBuilder.changeToNonPrivilegedConsole(NavigationActivity.this);
+
+ // Save preferences
+ Preferences.savePreference(
+ FileManagerSettings.SETTINGS_ACCESS_MODE,
+ AccessMode.PROMPT, true);
+
+ } catch (Exception e) {
+ // Displays an exception and exit
+ Log.e(TAG, getString(R.string.msgs_cant_create_console), e);
+ DialogHelper.showToast(
+ NavigationActivity.this,
+ R.string.msgs_cant_create_console, Toast.LENGTH_LONG);
+ exit();
+ }
+ }
+ });
+ DialogHelper.delegateDialogShow(this, dialog);
+ }
+
+ /**
+ * Method that creates a ChRooted environment, protecting the user to break anything in
+ * the device
+ * @hide
+ */
+ void createChRooted() {
+ // If we are in a ChRooted mode, then do nothing
+ if (this.mChRooted) return;
+ this.mChRooted = true;
+
+ //Change to first storage volume
+ Object[] volumes =
+ StorageHelper.getStorageVolumes(this);
+ if (volumes != null && volumes.length > 0) {
+ for (int x = 0; x < mPagerAdapter.getCount();x++) {
+ mPagerAdapter.getFragment(mViewPager, x).enterChRooted(
+ StorageHelper.getStoragePath(volumes[0]));
+ }
+ }
+
+ // Remove the history (don't allow to access to previous data)
+ clearHistory();
+ }
+
+ /**
+ * Method that exits from a ChRooted
+ * @hide
+ */
+ void exitChRooted() {
+ // If we aren't in a ChRooted mode, then do nothing
+ if (!this.mChRooted) return;
+ this.mChRooted = false;
+
+ for (int x = 0; x < mPagerAdapter.getCount();x++) {
+ NavigationFragment navigationFragment = (NavigationFragment) mPagerAdapter.getItem(x);
+ navigationFragment.exitChRooted();
+
+ }
+ }
+
+ /**
+ * Method that returns whether the activity is ChRooted
+ */
+ public boolean isChRooted(){
+ return this.mChRooted;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ /**
+ * A part of the PageChangeListener interface. Since there are numerous UI
+ * elements that are contextually based on the currently selected page, we
+ * need to trigger UI updates after the user swipes to a new page. The pager
+ * is contained within the main activity, but the relevant data must be
+ * pushed from the currently selected list fragment.
+ *
+ * @param position the integer index of the currently selected page
+ * @return nothing
+ */
+ @Override
+ public void onPageSelected(int position) {
+
+ // Tell the breadcrumb that the new fragment will now be the one sending dir changes
+ NavigationFragment navigationFragment = getCurrentNavigationFragment();
+ navigationFragment.setOnHistoryListener(navigationFragment);
+ navigationFragment.setOnNavigationOnRequestMenuListener(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onMarkFilesForPaste(List filesForPaste, COPY_MOVE_OPERATION pasteOperationType) {
+ this.mFilesForPaste = filesForPaste;
+ this.mPasteOperationType = pasteOperationType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onAreFilesMarkedForPaste() {
+ return this.mFilesForPaste != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onClearFilesMarkedForPaste() {
+ this.mFilesForPaste = null;
+ this.mPasteOperationType = null;
+ // Make sure that the paste action item is removed
+ invalidateOptionsMenu();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List onRequestFilesMarkedForPaste() {
+ return this.mFilesForPaste;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public COPY_MOVE_OPERATION onRequestPasteOperationType() {
+ return this.mPasteOperationType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String onRequestDestinationDir() {
+ return getCurrentNavigationFragment().getCurrentDir();
+ }
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, false);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/PickerActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/PickerActivity.java
new file mode 100644
index 000000000..b84de5577
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/PickerActivity.java
@@ -0,0 +1,682 @@
+/*
+ * 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 me.toolify.backbone.activities;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentActivity;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.FrameLayout;
+import android.widget.ListPopupWindow;
+import android.widget.ProgressBar;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.greenrobot.event.EventBus;
+import me.toolify.backbone.R;
+import me.toolify.backbone.adapters.CheckableListAdapter;
+import me.toolify.backbone.adapters.CheckableListAdapter.CheckableItem;
+import me.toolify.backbone.bus.events.FilesystemStatusUpdateEvent;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.fragments.NavigationFragment;
+import me.toolify.backbone.fragments.NavigationFragment.OnDirectoryChangedListener;
+import me.toolify.backbone.fragments.NavigationFragment.OnFilePickedListener;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.preferences.DisplayRestrictions;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.widgets.Breadcrumb;
+import me.toolify.backbone.ui.widgets.BreadcrumbItem;
+import me.toolify.backbone.ui.widgets.ButtonItem;
+import me.toolify.backbone.util.DialogHelper;
+import me.toolify.backbone.util.ExceptionUtil;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.StorageHelper;
+
+/**
+ * The activity for allow to use a {@link FragmentActivity} like, to pick a file from other
+ * application.
+ */
+public class PickerActivity extends AbstractNavigationActivity
+ implements OnCancelListener, OnDismissListener,
+ OnFilePickedListener, OnDirectoryChangedListener {
+
+ private static final String TAG = "PickerActivity"; //$NON-NLS-1$
+
+ private static boolean DEBUG = false;
+
+ private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
+ applyTheme();
+ }
+ }
+ }
+ };
+
+ // The result code
+ private static final int RESULT_CROP_IMAGE = 1;
+
+ // The component that holds the crop operation. We use Gallery3d because we are confidence
+ // of his input parameters
+ private static final ComponentName CROP_COMPONENT =
+ new ComponentName(
+ "com.android.gallery3d", //$NON-NLS-1$
+ "com.android.gallery3d.app.CropImage"); //$NON-NLS-1$
+
+ // Gallery crop editor action
+ private static final String ACTION_CROP = "com.android.camera.action.CROP"; //$NON-NLS-1$
+
+ // Extra data for Gallery CROP action
+ private static final String EXTRA_CROP = "crop"; //$NON-NLS-1$
+
+ // Scheme for file and directory picking
+ private static final String FILE_URI_SCHEME = "file"; //$NON-NLS-1$
+ private static final String FOLDER_URI_SCHEME = "folder"; //$NON-NLS-1$
+ private static final String DIRECTORY_URI_SCHEME = "directory"; //$NON-NLS-1$
+
+ FileSystemObject mFso; // The picked item
+ FileSystemObject mCurrentDirectory;
+ private AlertDialog mDialog;
+ /**
+ * @hide
+ */
+ NavigationFragment mNavigationFragment;
+ Breadcrumb mBreadcrumb;
+ private View mRootView;
+ private ButtonItem mFilesystemInfo;
+ private ProgressBar mFilesystemInfoRefreshing;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCreate(Bundle state) {
+ //Save state
+ super.onCreate(state);
+
+ if (DEBUG) {
+ Log.d(TAG, "PickerActivity.onCreate"); //$NON-NLS-1$
+ }
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+ registerReceiver(this.mNotificationReceiver, filter);
+
+ // Initialize the activity
+ init();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "PickerActivity.onDestroy"); //$NON-NLS-1$
+ }
+
+ // Unregister the receiver
+ try {
+ unregisterReceiver(this.mNotificationReceiver);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPause() {
+ super.onPause();
+ // Always unregister when an object no longer should be on the bus.
+ EventBus.getDefault().unregister(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ EventBus.getDefault().register(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ measureHeight();
+ }
+
+ /**
+ * Method that displays a dialog with a {@link NavigationFragment} to select the
+ * proposed file
+ */
+ private void init() {
+ final boolean pickingDirectory;
+ final Intent intent = getIntent();
+
+ if (isFilePickIntent(intent)) {
+ // ok
+ Log.d(TAG, "PickerActivity: got file pick intent: " + String.valueOf(intent)); //$NON-NLS-1$
+ pickingDirectory = false;
+ } else if (isDirectoryPickIntent(getIntent())) {
+ // ok
+ Log.d(TAG, "PickerActivity: got folder pick intent: " + String.valueOf(intent)); //$NON-NLS-1$
+ pickingDirectory = true;
+ } else {
+ Log.d(TAG, "PickerActivity got unrecognized intent: " + String.valueOf(intent)); //$NON-NLS-1$
+ setResult(FragmentActivity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ // Display restrictions
+ Map restrictions = new HashMap();
+ //- Mime/Type restriction
+ String mimeType = getIntent().getType();
+ if (mimeType != null) {
+ if (!MimeTypeHelper.isMimeTypeKnown(this, mimeType)) {
+ Log.i(TAG,
+ String.format(
+ "Mime type %s unknown, falling back to wildcard.", //$NON-NLS-1$
+ mimeType));
+ mimeType = MimeTypeHelper.ALL_MIME_TYPES;
+ }
+ restrictions.put(DisplayRestrictions.MIME_TYPE_RESTRICTION, mimeType);
+ }
+ // Other restrictions
+ Bundle extras = getIntent().getExtras();
+ Log.d(TAG, "PickerActivity. extras: " + String.valueOf(extras)); //$NON-NLS-1$
+ if (extras != null) {
+ //-- File size
+ if (extras.containsKey(android.provider.MediaStore.Audio.Media.EXTRA_MAX_BYTES)) {
+ long size =
+ extras.getLong(android.provider.MediaStore.Audio.Media.EXTRA_MAX_BYTES);
+ restrictions.put(DisplayRestrictions.SIZE_RESTRICTION, Long.valueOf(size));
+ }
+ //-- Local filesystems only
+ if (extras.containsKey(Intent.EXTRA_LOCAL_ONLY)) {
+ boolean localOnly = extras.getBoolean(Intent.EXTRA_LOCAL_ONLY);
+ restrictions.put(
+ DisplayRestrictions.LOCAL_FILESYSTEM_ONLY_RESTRICTION,
+ Boolean.valueOf(localOnly));
+ }
+ }
+ if (pickingDirectory) {
+ restrictions.put(DisplayRestrictions.DIRECTORY_ONLY_RESTRICTION, Boolean.TRUE);
+ }
+
+ // Create or use the console
+ if (!initializeConsole()) {
+ // Something when wrong. Display a message and exit
+ DialogHelper.showToast(this, R.string.msgs_cant_create_console, Toast.LENGTH_SHORT);
+ cancel();
+ return;
+ }
+
+ // Create the root file
+ this.mRootView = getLayoutInflater().inflate(R.layout.picker, null, false);
+ this.mRootView.post(new Runnable() {
+ @Override
+ public void run() {
+ measureHeight();
+ }
+ });
+
+ // Initialize the (pseudo) action bar
+ updateTitleActionBar();
+
+ // Navigation view
+ this.mNavigationFragment =
+ (NavigationFragment)getFragmentManager().findFragmentById (R.id.navigation_fragment);
+ this.mNavigationFragment.setRestrictions(restrictions);
+ this.mNavigationFragment.setOnFilePickedListener(this);
+ this.mNavigationFragment.setOnDirectoryChangedListener(this);
+
+ // Apply the current theme
+ applyTheme();
+
+ // Create the dialog
+ this.mDialog = DialogHelper.createDialog(
+ this, R.drawable.ic_launcher,
+ pickingDirectory ? R.string.directory_picker_title : R.string.picker_title,
+ this.mRootView);
+
+ this.mDialog.setButton(
+ DialogInterface.BUTTON_NEGATIVE,
+ getString(R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dlg, int which) {
+ dlg.cancel();
+ }
+ });
+ if (pickingDirectory) {
+ this.mDialog.setButton(
+ DialogInterface.BUTTON_POSITIVE,
+ getString(R.string.select),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dlg, int which) {
+ PickerActivity.this.mFso = PickerActivity.this.mCurrentDirectory;
+ dlg.dismiss();
+ }
+ });
+ }
+ this.mDialog.setCancelable(true);
+ this.mDialog.setOnCancelListener(this);
+ this.mDialog.setOnDismissListener(this);
+ DialogHelper.delegateDialogShow(this, this.mDialog);
+
+ // Set content description of storage volume button
+ mFilesystemInfo = (ButtonItem)this.mRootView.findViewById(R.id.button_filesystem_info);
+ mFilesystemInfo.setContentDescription(getString(R.string.actionbar_button_storage_cd));
+ mFilesystemInfoRefreshing = (ProgressBar)this.mRootView.findViewById(
+ R.id.button_filesystem_info_refreshing);
+
+ }
+
+ /**
+ * Method that measure the height needed to avoid resizing when
+ * change to a new directory. This method fixed the height of the window
+ * @hide
+ */
+ void measureHeight() {
+ // Calculate the dialog size based on the window height
+ DisplayMetrics displaymetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
+ final int height = displaymetrics.heightPixels;
+
+ Configuration config = getResources().getConfiguration();
+ int percent = config.orientation == Configuration.ORIENTATION_LANDSCAPE ? 55 : 70;
+
+ FrameLayout.LayoutParams params =
+ new FrameLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, (height * percent) / 100);
+ this.mRootView.setLayoutParams(params);
+ }
+
+ /**
+ * Method that initializes a console
+ */
+ private boolean initializeConsole() {
+ try {
+ // Create a ChRooted console
+ ConsoleBuilder.createDefaultConsole(this, false, false);
+ // There is a console allocated. Use it.
+ return true;
+ } catch (Throwable _throw) {
+ // Capture the exception
+ ExceptionUtil.translateException(this, _throw, true, false);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CROP_IMAGE:
+ // Return what the callee activity returns
+ setResult(resultCode, data);
+ finish();
+ return;
+
+ default:
+ break;
+ }
+
+ // The response is not understood
+ Log.w(TAG,
+ String.format(
+ "Ignore response. requestCode: %s, resultCode: %s, data: %s", //$NON-NLS-1$
+ Integer.valueOf(requestCode),
+ Integer.valueOf(resultCode),
+ data));
+ DialogHelper.showToast(this, R.string.msgs_operation_failure, Toast.LENGTH_SHORT);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (this.mFso != null) {
+ File src = new File(this.mFso.getFullPath());
+ if (getIntent().getExtras() != null) {
+ // Some AOSP applications use the gallery to edit and crop the selected image
+ // with the Gallery crop editor. In this case pass the picked file to the
+ // CropActivity with the requested parameters
+ // Expected result is on onActivityResult
+ Bundle extras = getIntent().getExtras();
+ String crop = extras.getString(EXTRA_CROP);
+ if (Boolean.parseBoolean(crop)) {
+ // We want to use the Gallery3d activity because we know about it, and his
+ // parameters. At least we have a compatible one.
+ Intent intent = new Intent(ACTION_CROP);
+ if (getIntent().getType() != null) {
+ intent.setType(getIntent().getType());
+ }
+ intent.setData(Uri.fromFile(src));
+ intent.putExtras(extras);
+ intent.setComponent(CROP_COMPONENT);
+ startActivityForResult(intent, RESULT_CROP_IMAGE);
+ return;
+ }
+ }
+
+ // Return the picked file, as expected (this activity should fill the intent data
+ // and return RESULT_OK result)
+ Intent result = new Intent();
+ result.setData(getResultUriForFileFromIntent(src, getIntent()));
+ setResult(FragmentActivity.RESULT_OK, result);
+ finish();
+
+ } else {
+ cancel();
+ }
+ }
+
+ private static boolean isFilePickIntent(Intent intent) {
+ final String action = intent.getAction();
+
+ if (Intent.ACTION_GET_CONTENT.equals(action)) {
+ return true;
+ }
+ if (Intent.ACTION_PICK.equals(action)) {
+ final Uri data = intent.getData();
+ if (data != null && FILE_URI_SCHEME.equals(data.getScheme())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isDirectoryPickIntent(Intent intent) {
+ if (Intent.ACTION_PICK.equals(intent.getAction()) && intent.getData() != null) {
+ String scheme = intent.getData().getScheme();
+ if (FOLDER_URI_SCHEME.equals(scheme) || DIRECTORY_URI_SCHEME.equals(scheme)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Uri getResultUriForFileFromIntent(File src, Intent intent) {
+ Uri result = Uri.fromFile(src);
+
+ if (Intent.ACTION_PICK.equals(intent.getAction()) && intent.getData() != null) {
+ String scheme = intent.getData().getScheme();
+ if (scheme != null) {
+ result = result.buildUpon().scheme(scheme).build();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ cancel();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFilePicked(FileSystemObject item) {
+ this.mFso = item;
+ this.mDialog.dismiss();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDirectoryChanged(FileSystemObject item) {
+ this.mCurrentDirectory = item;
+ }
+
+ /**
+ * This method is called by various pieces of code responsible for updating file listing or
+ * breadcrumb data. The method receives all {@link me.toolify.backbone.bus.events.FilesystemStatusUpdateEvent}
+ * events generated by the various asyncTasks responsible for gathering and sending file info.
+ *
+ * @param event an event containing the filesystem status code
+ */
+ public void onEvent(FilesystemStatusUpdateEvent event) {
+ setFilesystemStatusDrawable(event.status);
+ }
+
+ private void setFilesystemStatusDrawable(int fileSystemstatus){
+
+ TypedArray a = getTheme().obtainStyledAttributes(R.styleable.FileManager);
+
+ switch (fileSystemstatus) {
+
+ case FilesystemStatusUpdateEvent.INDICATOR_UNLOCKED:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setImageResource(
+ a.getResourceId(R.styleable.FileManager_actionIconLockOpen,
+ R.drawable.ic_action_holo_dark_lock_open));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_LOCKED:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setImageResource(
+ a.getResourceId(R.styleable.FileManager_actionIconLockClosed,
+ R.drawable.ic_action_holo_dark_lock_closed));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_WARNING:
+ if (mFilesystemInfo != null) {
+ mFilesystemInfo.setImageResource(
+ a.getResourceId(R.styleable.FileManager_actionIconWarning,
+ R.drawable.ic_action_holo_dark_warning));
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_REFRESHING:
+ if (mFilesystemInfo != null) {
+ setFilesystemInfoProgressState(true);
+ }
+ break;
+
+ case FilesystemStatusUpdateEvent.INDICATOR_STOP_REFRESHING:
+ if (mFilesystemInfo != null) {
+ setFilesystemInfoProgressState(false);
+ }
+ break;
+ }
+
+ a.recycle();
+ }
+
+ /**
+ * This function switches an action item between its normal icon and an indeterminate progress
+ * circle
+ * @param refreshing value is true if the action item should show the progress bar
+ */
+ public void setFilesystemInfoProgressState(final boolean refreshing) {
+ if (mFilesystemInfo != null) {
+
+ if (refreshing) {
+ mFilesystemInfo.setVisibility(View.INVISIBLE);
+ mFilesystemInfoRefreshing.setVisibility(View.VISIBLE);
+ } else {
+ mFilesystemInfo.setVisibility(View.VISIBLE);
+ mFilesystemInfoRefreshing.setVisibility(View.INVISIBLE);
+ }
+
+ }
+ }
+
+ /**
+ * Method invoked when an action item is clicked.
+ *
+ * @param view The button pushed
+ */
+ public void onActionBarItemClick(View view) {
+ switch (view.getId()) {
+ //######################
+ //Breadcrumb Actions
+ //######################
+ case R.id.button_filesystem_info:
+ //Show a popup with the storage volumes to select
+ showStorageVolumesPopUp(view);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Method that cancels the activity
+ */
+ private void cancel() {
+ setResult(FragmentActivity.RESULT_CANCELED);
+ finish();
+ }
+
+ /**
+ * Method that shows a popup with the storage volumes
+ *
+ * @param anchor The view on which anchor the popup
+ */
+ private void showStorageVolumesPopUp(View anchor) {
+ // Create a list (but not checkable)
+ final Object[] volumes = StorageHelper.getStorageVolumes(PickerActivity.this);
+ List descriptions = new ArrayList();
+ if (volumes != null) {
+ int cc = volumes.length;
+ for (int i = 0; i < cc; i++) {
+ String desc = StorageHelper.getStorageVolumeDescription(this, volumes[i]);
+ CheckableItem item = new CheckableItem(desc, false, false);
+ descriptions.add(item);
+ }
+ }
+ CheckableListAdapter adapter =
+ new CheckableListAdapter(getApplicationContext(), descriptions);
+
+ //Create a show the popup menu
+ final ListPopupWindow popup = DialogHelper.createListPopupWindow(this, adapter, anchor);
+ popup.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View v, int position, long id) {
+ popup.dismiss();
+ if (volumes != null) {
+ PickerActivity.this.
+ mNavigationFragment.changeCurrentDir(
+ StorageHelper.getStoragePath(volumes[position]));
+ }
+ }
+ });
+ popup.show();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void updateTitleActionBar() {
+ mBreadcrumb = (Breadcrumb)this.mRootView.findViewById(R.id.breadcrumb_view);
+ // Set the free disk space warning level of the breadcrumb widget
+ String fds = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
+ (String)FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
+ mBreadcrumb.setFreeDiskSpaceWarningLevel(Integer.parseInt(fds));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void pairBreadcrumb(int position, NavigationFragment fragment) {
+ try {
+ fragment.setBreadcrumb(mBreadcrumb);
+ } catch (Throwable ex) {
+ Log.e(TAG,
+ String.format("Failed to pair breadcrumb %d", //$NON-NLS-1$
+ position, ex));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onBreadcrumbItemClick(File item) {
+ this.mNavigationFragment.changeCurrentDir(item.getAbsolutePath());
+ }
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, true);
+ this.mNavigationFragment.applyTheme();
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/SearchActivity.java
similarity index 85%
rename from src/com/cyanogenmod/filemanager/activities/SearchActivity.java
rename to Backbone/src/main/java/me/toolify/backbone/activities/SearchActivity.java
index 60d043c82..8d7b5d080 100644
--- a/src/com/cyanogenmod/filemanager/activities/SearchActivity.java
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/SearchActivity.java
@@ -1,5 +1,6 @@
/*
- * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2012-2013 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +15,8 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.activities;
+package me.toolify.backbone.activities;
-import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.SearchManager;
@@ -34,56 +34,54 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
-import android.widget.TextView;
import android.widget.Toast;
-import com.cyanogenmod.filemanager.FileManagerApplication;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences;
-import com.cyanogenmod.filemanager.activities.preferences.SettingsPreferences.SearchPreferenceFragment;
-import com.cyanogenmod.filemanager.adapters.SearchResultAdapter;
-import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
-import com.cyanogenmod.filemanager.console.RelaunchableException;
-import com.cyanogenmod.filemanager.listeners.OnRequestRefreshListener;
-import com.cyanogenmod.filemanager.model.Directory;
-import com.cyanogenmod.filemanager.model.FileSystemObject;
-import com.cyanogenmod.filemanager.model.ParentDirectory;
-import com.cyanogenmod.filemanager.model.Query;
-import com.cyanogenmod.filemanager.model.SearchResult;
-import com.cyanogenmod.filemanager.model.Symlink;
-import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
-import com.cyanogenmod.filemanager.preferences.AccessMode;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider;
-import com.cyanogenmod.filemanager.tasks.SearchResultDrawingAsyncTask;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
-import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
-import com.cyanogenmod.filemanager.ui.dialogs.MessageProgressDialog;
-import com.cyanogenmod.filemanager.ui.policy.DeleteActionPolicy;
-import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
-import com.cyanogenmod.filemanager.ui.widgets.ButtonItem;
-import com.cyanogenmod.filemanager.ui.widgets.FlingerListView;
-import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerListener;
-import com.cyanogenmod.filemanager.ui.widgets.FlingerListView.OnItemFlingerResponder;
-import com.cyanogenmod.filemanager.util.CommandHelper;
-import com.cyanogenmod.filemanager.util.DialogHelper;
-import com.cyanogenmod.filemanager.util.ExceptionUtil;
-import com.cyanogenmod.filemanager.util.ExceptionUtil.OnRelaunchCommandResult;
-import com.cyanogenmod.filemanager.util.FileHelper;
-import com.cyanogenmod.filemanager.util.StorageHelper;
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.activities.preferences.SettingsPreferences;
+import me.toolify.backbone.activities.preferences.SearchPreferenceFragment;
+import me.toolify.backbone.adapters.SearchResultAdapter;
+import me.toolify.backbone.commands.AsyncResultExecutable;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.console.RelaunchableException;
+import me.toolify.backbone.listeners.OnRequestRefreshListener;
+import me.toolify.backbone.model.Directory;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.ParentDirectory;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.model.SearchResult;
+import me.toolify.backbone.model.Symlink;
+import me.toolify.backbone.parcelables.SearchInfoParcelable;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.providers.RecentSearchesContentProvider;
+import me.toolify.backbone.tasks.SearchResultDrawingAsyncTask;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.dialogs.MessageProgressDialog;
+import me.toolify.backbone.ui.policy.DeleteActionPolicy;
+import me.toolify.backbone.ui.policy.IntentsActionPolicy;
+import me.toolify.backbone.ui.widgets.FlingerListView;
+import me.toolify.backbone.ui.widgets.FlingerListView.OnItemFlingerListener;
+import me.toolify.backbone.ui.widgets.FlingerListView.OnItemFlingerResponder;
+import me.toolify.backbone.util.CommandHelper;
+import me.toolify.backbone.util.DialogHelper;
+import me.toolify.backbone.util.ExceptionUtil;
+import me.toolify.backbone.util.ExceptionUtil.OnRelaunchCommandResult;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.StorageHelper;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -104,7 +102,7 @@ public class SearchActivity extends Activity
* An {@link Intent} action for restore view information.
*/
public static final String ACTION_RESTORE =
- "com.cyanogenmod.filemanager.activities.SearchActivity#Restore"; //$NON-NLS-1$
+ "me.toolify.backbone.activities.SearchActivity#Restore"; //$NON-NLS-1$
/**
* Intent extra parameter for search in the selected directory on enter.
@@ -168,8 +166,7 @@ public boolean onItemFlingerStart(
AdapterView> parent, View view, int position, long id) {
try {
// Response if the item can be removed
- SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
- SearchResult result = adapter.getItem(position);
+ SearchResult result = mSearchResultAdaper.getItem(position);
if (result != null && result.getFso() != null) {
if (result.getFso() instanceof ParentDirectory) {
// This is not possible ...
@@ -189,8 +186,7 @@ public void onItemFlingerEnd(OnItemFlingerResponder responder,
try {
// Response if the item can be removed
- SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
- SearchResult result = adapter.getItem(position);
+ SearchResult result = mSearchResultAdaper.getItem(position);
if (result != null && result.getFso() != null) {
DeleteActionPolicy.removeFileSystemObject(
SearchActivity.this,
@@ -227,15 +223,16 @@ public void onItemFlingerEnd(OnItemFlingerResponder responder,
/**
* @hide
*/
- ProgressBar mSearchWaiting;
+ SearchResultAdapter mSearchResultAdaper;
/**
* @hide
*/
- TextView mSearchFoundItems;
+ ProgressBar mSearchWaiting;
/**
* @hide
*/
- TextView mSearchTerms;
+ MenuItem mSearchFoundItems;
+
private View mEmptyListMsg;
private String mSearchDirectory;
@@ -410,21 +407,48 @@ private void restoreState(Bundle state) {
*/
private void initTitleActionBar() {
//Configure the action bar options
- getActionBar().setBackgroundDrawable(
- getResources().getDrawable(R.drawable.bg_holo_titlebar));
- getActionBar().setDisplayOptions(
- ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
getActionBar().setDisplayHomeAsUpEnabled(true);
- View customTitle = getLayoutInflater().inflate(R.layout.simple_customtitle, null, false);
+ getActionBar().setTitle(R.string.search);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.search, menu);
+ mSearchFoundItems = menu.findItem(R.id.mnu_search_result_number);
+ return super.onCreateOptionsMenu(menu);
+ }
- TextView title = (TextView)customTitle.findViewById(R.id.customtitle_title);
- title.setText(R.string.search);
- title.setContentDescription(getString(R.string.search));
- ButtonItem configuration = (ButtonItem)customTitle.findViewById(R.id.ab_button1);
- configuration.setImageResource(R.drawable.ic_holo_light_config);
- configuration.setVisibility(View.VISIBLE);
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ return super.onPrepareOptionsMenu(menu);
+ }
- getActionBar().setCustomView(customTitle);
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ back(true, null, false);
+ break;
+ case R.id.mnu_search_settings:
+ //Settings
+ Intent settings = new Intent(this, SettingsPreferences.class);
+ settings.putExtra(
+ PreferenceActivity.EXTRA_SHOW_FRAGMENT,
+ SearchPreferenceFragment.class.getName());
+ startActivity(settings);
+ break;
+ }
+ return super.onOptionsItemSelected(item);
}
/**
@@ -452,32 +476,13 @@ private void initComponents() {
//Other components
this.mSearchWaiting = (ProgressBar)findViewById(R.id.search_waiting);
- this.mSearchFoundItems = (TextView)findViewById(R.id.search_status_found_items);
+ /*TODO: Set this info to the status action icon when "in progress" searching is focused on the list view and
+ not the dialog * /
+/* this.mSearchFoundItems = (TextView)findViewById(R.id.search_status_found_items);
setFoundItems(0, ""); //$NON-NLS-1$
this.mSearchTerms = (TextView)findViewById(R.id.search_status_query_terms);
this.mSearchTerms.setText(
- Html.fromHtml(getString(R.string.search_terms, ""))); //$NON-NLS-1$
- }
-
- /**
- * Method invoked when an action item is clicked.
- *
- * @param view The button pushed
- */
- public void onActionBarItemClick(View view) {
- switch (view.getId()) {
- case R.id.ab_button1:
- //Settings
- Intent settings = new Intent(this, SettingsPreferences.class);
- settings.putExtra(
- PreferenceActivity.EXTRA_SHOW_FRAGMENT,
- SearchPreferenceFragment.class.getName());
- startActivity(settings);
- break;
-
- default:
- break;
- }
+ Html.fromHtml(getString(R.string.search_terms, ""))); //$NON-NLS-1$*/
}
/**
@@ -575,7 +580,7 @@ public void onClick(DialogInterface alertDialog, int which) {
//Close search activity
back(true, null, false);
}
- });
+ });
DialogHelper.delegateDialogShow(this, dialog);
}
@@ -611,15 +616,17 @@ void doSearch(
}
//Set the listview
+ if (this.mSearchListView.getAdapter() != null) {
+ ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
+ }
this.mResultList = new ArrayList();
SearchResultAdapter adapter =
new SearchResultAdapter(this,
new ArrayList(), R.layout.search_item, this.mQuery);
this.mSearchListView.setAdapter(adapter);
- //Set terms
- this.mSearchTerms.setText(
- Html.fromHtml(getString(R.string.search_terms, query.getTerms())));
+ // Set terms in action bar title
+ getActionBar().setTitle(getString(R.string.search) + ": \"" + query.getTerms() + "\""); //$NON-NLS-1$
//Now, do the search in background
this.mSearchListView.post(new Runnable() {
@@ -719,8 +726,7 @@ public void run() {
if (terms.endsWith(" | ")) { //$NON-NLS-1$;
terms = ""; //$NON-NLS-1$;
}
- SearchActivity.this.mSearchTerms.setText(
- Html.fromHtml(getString(R.string.search_terms, terms)));
+ getActionBar().setTitle(getString(R.string.search) + ": \"" + query.getTerms() + "\""); //$NON-NLS-1$
try {
if (SearchActivity.this.mSearchWaiting != null) {
@@ -783,7 +789,6 @@ private List filterQuery(List original) {
void removeAll() {
SearchResultAdapter adapter = (SearchResultAdapter)this.mSearchListView.getAdapter();
adapter.clear();
- adapter.notifyDataSetChanged();
this.mSearchListView.setSelection(0);
toggleResults(false, true);
}
@@ -808,28 +813,27 @@ void toggleResults(boolean hasResults, boolean showEmpty) {
* @hide
*/
void setFoundItems(final int items, final String searchDirectory) {
- if (this.mSearchFoundItems != null) {
- this.mSearchFoundItems.post(new Runnable() {
- @Override
- public void run() {
- String directory = searchDirectory;
- if (SearchActivity.this.mChRooted &&
- directory != null && directory.length() > 0) {
- directory = StorageHelper.getChrootedPath(directory);
- }
-
- String foundItems =
- getResources().
+ this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String directory = searchDirectory;
+ if (SearchActivity.this.mChRooted &&
+ directory != null && directory.length() > 0) {
+ directory = StorageHelper.getChrootedPath(directory);
+ }
+ String foundItems =
+ getResources().
getQuantityString(
- R.plurals.search_found_items, items, Integer.valueOf(items));
- SearchActivity.this.mSearchFoundItems.setText(
- getString(
- R.string.search_found_items_in_directory,
- foundItems,
- directory));
+ R.plurals.search_found_items, items, Integer.valueOf(items));
+ if (mSearchFoundItems != null) {
+ mSearchFoundItems.setTitle(
+ getString(
+ R.string.search_found_items_in_directory,
+ foundItems,
+ directory));
}
- });
- }
+ }
+ });
}
/**
@@ -846,20 +850,6 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- back(true, null, false);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
/**
* {@inheritDoc}
*/
@@ -930,10 +920,6 @@ public void onRequestMenu(FileSystemObject item) {
}
return;
}
-
- ActionsDialog dialog = new ActionsDialog(this, fso, false, true);
- dialog.setOnRequestRefreshListener(this);
- dialog.show();
}
/**
@@ -1003,7 +989,7 @@ public void onRequestRemove(Object o, boolean clearSelection) {
@Override
public void onNavigateTo(Object o) {
if (o instanceof FileSystemObject) {
- back(false, (FileSystemObject)o, true);
+ back(false, (FileSystemObject) o, true);
}
}
@@ -1047,7 +1033,7 @@ void back(final boolean cancelled, FileSystemObject item, boolean isChecked) {
@Override
public void onSuccess() {
if (navigateTo(fFso, intent)) {
- finish();
+ exit();
}
}
@Override
@@ -1072,8 +1058,18 @@ public void onCancelled() { /** NON BLOCK**/}
// End this activity
if (finish) {
- finish();
+ exit();
+ }
+ }
+
+ /**
+ * Method invoked when the activity needs to exit
+ */
+ private void exit() {
+ if (this.mSearchListView.getAdapter() != null) {
+ ((SearchResultAdapter)this.mSearchListView.getAdapter()).dispose();
}
+ finish();
}
/**
@@ -1244,22 +1240,9 @@ void applyTheme() {
Theme theme = ThemeManager.getCurrentTheme(this);
theme.setBaseTheme(this, false);
- //- ActionBar
- theme.setTitlebarDrawable(this, getActionBar(), "titlebar_drawable"); //$NON-NLS-1$
- View v = getActionBar().getCustomView().findViewById(R.id.customtitle_title);
- theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
- v = findViewById(R.id.ab_button1);
- theme.setImageDrawable(this, (ImageView)v, "ic_config_drawable"); //$NON-NLS-1$
// ContentView
theme.setBackgroundDrawable(
this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$
- //- StatusBar
- v = findViewById(R.id.search_status);
- theme.setBackgroundDrawable(this, v, "statusbar_drawable"); //$NON-NLS-1$
- v = findViewById(R.id.search_status_found_items);
- theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
- v = findViewById(R.id.search_status_query_terms);
- theme.setTextColor(this, (TextView)v, "text_color"); //$NON-NLS-1$
//ListView
if (this.mSearchListView.getAdapter() != null) {
((SearchResultAdapter)this.mSearchListView.getAdapter()).notifyDataSetChanged();
diff --git a/src/com/cyanogenmod/filemanager/activities/ShortcutActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/ShortcutActivity.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/activities/ShortcutActivity.java
rename to Backbone/src/main/java/me/toolify/backbone/activities/ShortcutActivity.java
index cf4377066..6807d24ba 100644
--- a/src/com/cyanogenmod/filemanager/activities/ShortcutActivity.java
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/ShortcutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.activities;
+package me.toolify.backbone.activities;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -28,16 +28,16 @@
import android.util.Log;
import android.widget.Toast;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.console.ConsoleBuilder;
-import com.cyanogenmod.filemanager.model.FileSystemObject;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
-import com.cyanogenmod.filemanager.ui.policy.IntentsActionPolicy;
-import com.cyanogenmod.filemanager.util.CommandHelper;
-import com.cyanogenmod.filemanager.util.DialogHelper;
-import com.cyanogenmod.filemanager.util.ExceptionUtil;
+import me.toolify.backbone.R;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.policy.IntentsActionPolicy;
+import me.toolify.backbone.util.CommandHelper;
+import me.toolify.backbone.util.DialogHelper;
+import me.toolify.backbone.util.ExceptionUtil;
/**
* The activity for handle the desktop shortcuts create by the app.
@@ -200,11 +200,7 @@ private void init() {
*/
private boolean initializeConsole() {
try {
- // Is there a console allocate
- if (!ConsoleBuilder.isAlloc()) {
- // Create a console
- ConsoleBuilder.getConsole(this);
- }
+ ConsoleBuilder.getConsole(this);
// There is a console allocated. Use it.
return true;
} catch (Throwable _throw) {
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/AboutPreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/AboutPreferenceFragment.java
new file mode 100644
index 000000000..bdf1c73d3
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/AboutPreferenceFragment.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.activities.ChangeLogActivity;
+import me.toolify.backbone.preferences.Preferences;
+
+/**
+ * A class that manages the 'about' screen
+ */
+public class AboutPreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "AboutPreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private Preference mFaq;
+ private Preference mChangelog;
+ private Preference mPrivacyPolicy;
+ private Preference mCreditsAndLicenses;
+ private Preference mVersion;
+
+ /**
+ * @hide
+ */
+ boolean mLoaded = false;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+ this.mLoaded = false;
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_about);
+
+ // Frequently Asked Questions
+ this.mFaq = findPreference("bb_filemanager_faq");
+ this.mFaq.setEnabled(false);
+
+ // Changelog
+ this.mChangelog = findPreference("bb_filemanager_changelog");
+ mChangelog.setIntent(new Intent(getActivity(), ChangeLogActivity.class));
+ this.mChangelog.setEnabled(false);
+
+ // Privacy Policy
+ this.mPrivacyPolicy = findPreference("bb_filemanager_privacy_policy");
+ this.mPrivacyPolicy.setEnabled(false);
+
+ // Credits and Open Source Licenses
+ this.mCreditsAndLicenses = findPreference("bb_filemanager_credits_licenses");
+ mCreditsAndLicenses.setIntent(new Intent(getActivity(), LicenseActivity.class));;
+
+ // Build Version
+ this.mVersion = findPreference("bb_filemanager_version");
+ // Retrieve the about header
+ try {
+ String appver =
+ getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName;
+ mVersion.setSummary(getString(R.string.pref_about_version_num, appver));
+ } catch (Exception e) {
+ mVersion.setSummary(getString(R.string.pref_about_version_num, "")); //$NON-NLS-1$
+ }
+
+ // Loaded
+ this.mLoaded = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_about);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorPreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorPreferenceFragment.java
new file mode 100644
index 000000000..76b956bf0
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorPreferenceFragment.java
@@ -0,0 +1,129 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.util.Log;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+
+/**
+ * A class that manages the editor options
+ */
+public class EditorPreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "EditorPreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private CheckBoxPreference mNoSuggestions;
+ private CheckBoxPreference mWordWrap;
+ private CheckBoxPreference mHexdump;
+
+ private CheckBoxPreference mSyntaxHighlight;
+
+
+ /**
+ * @hide
+ */
+ boolean mLoaded = false;
+
+ private final OnPreferenceChangeListener mOnChangeListener =
+ new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(final Preference preference, Object newValue) {
+ boolean ret = true;
+
+ String key = preference.getKey();
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("New value for %s: %s", //$NON-NLS-1$
+ key,
+ String.valueOf(newValue)));
+ }
+
+ // Notify the change (only if fragment is loaded. Default values are loaded
+ // while not in loaded mode)
+ if (EditorPreferenceFragment.this.mLoaded && ret) {
+ Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED);
+ intent.putExtra(
+ FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey());
+ getActivity().sendBroadcast(intent);
+ }
+
+ return ret;
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+ this.mLoaded = false;
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_editor);
+
+ // No suggestions
+ this.mNoSuggestions =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_EDITOR_NO_SUGGESTIONS.getId());
+ this.mNoSuggestions.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // WordWrap
+ this.mWordWrap =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_EDITOR_WORD_WRAP.getId());
+ this.mWordWrap.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Hexdump
+ this.mHexdump =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_EDITOR_HEXDUMP.getId());
+ this.mHexdump.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Syntax highlight
+ this.mSyntaxHighlight =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_EDITOR_SYNTAX_HIGHLIGHT.getId());
+ this.mSyntaxHighlight.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Loaded
+ this.mLoaded = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_editor);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorSHColorSchemePreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorSHColorSchemePreferenceFragment.java
new file mode 100644
index 000000000..eba18b2db
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/EditorSHColorSchemePreferenceFragment.java
@@ -0,0 +1,308 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.text.TextUtils;
+import android.util.Log;
+
+import me.toolify.backbone.R;
+import com.ash.syntaxhighlight.HighlightColors;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.preferences.ColorPickerPreference;
+import me.toolify.backbone.util.ExceptionUtil;
+
+/**
+ * A class that manages the color scheme of the syntax highlight processor.
+ */
+public class EditorSHColorSchemePreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "EditorSHColorSchemePreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private static final String KEY_RESET_COLOR_SCHEME = "ash_reset_color_scheme"; //$NON-NLS-1$
+
+ private CheckBoxPreference mUseThemeDefault;
+ private Preference mResetColorScheme;
+ private ColorPickerPreference[] mColorScheme;
+
+ /**
+ * @hide
+ */
+ boolean mLoaded = false;
+
+ private final OnPreferenceChangeListener mOnChangeListener =
+ new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(final Preference preference, final Object newValue) {
+ boolean ret = true;
+
+ String key = preference.getKey();
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("New value for %s: %s", //$NON-NLS-1$
+ key,
+ String.valueOf(newValue)));
+ }
+
+ // Use theme default
+ if (key.compareTo(
+ FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId()) == 0) {
+ boolean enabled = ((Boolean)newValue).booleanValue();
+ setColorSchemeEnabled(!enabled);
+
+ } else if (isColorSchemePreference(preference)) {
+ // Unify the color schemes property. Save the property here
+ int color = ((Integer)newValue).intValue();
+ try {
+ String colorScheme = toColorSchemeSet(preference, color);
+ Preferences.savePreference(
+ FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME,
+ colorScheme,
+ true);
+ } catch (Exception e) {
+ ExceptionUtil.translateException(getActivity(), e);
+ }
+ ((ColorPickerPreference)preference).setColor(color);
+
+ // Change the key to get notifications of color scheme
+ key = FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId();
+ }
+
+ // Notify the change (only if fragment is loaded. Default values are loaded
+ // while not in loaded mode)
+ if (EditorSHColorSchemePreferenceFragment.this.mLoaded && ret) {
+ Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED);
+ intent.putExtra(
+ FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, key);
+ getActivity().sendBroadcast(intent);
+ }
+
+ return ret;
+ }
+ };
+
+ private final OnPreferenceClickListener mOnClickListener =
+ new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String key = preference.getKey();
+ if (KEY_RESET_COLOR_SCHEME.compareTo(key) == 0) {
+ loadDefaultColorScheme(true);
+ }
+ return false;
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_editor_color_scheme);
+
+ // Color scheme (need to resolver color scheme prior to use theme default)
+ loadDefaultColorScheme(false);
+
+ // Use Theme default
+ this.mUseThemeDefault =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId());
+ Boolean defaultValue = ((Boolean)FileManagerSettings.
+ SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getDefaultValue());
+ Boolean value =
+ Boolean.valueOf(
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_EDITOR_SH_USE_THEME_DEFAULT.getId(),
+ defaultValue.booleanValue()));
+
+ // Reset to default theme color scheme
+ this.mResetColorScheme = findPreference(KEY_RESET_COLOR_SCHEME);
+
+ // Now the listeners
+ this.mOnChangeListener.onPreferenceChange(this.mUseThemeDefault, value);
+ this.mUseThemeDefault.setOnPreferenceChangeListener(this.mOnChangeListener);
+ this.mResetColorScheme.setOnPreferenceClickListener(this.mOnClickListener);
+
+ // Loaded
+ this.mLoaded = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_syntax_highlight_color_scheme);
+ }
+
+ /**
+ * Method that loads the default color scheme
+ *
+ * @param reset Whether the color scheme should be reseted
+ * @hide
+ */
+ void loadDefaultColorScheme(boolean reset) {
+ try {
+ String defaultValue =
+ (String)FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getDefaultValue();
+ if (!reset) {
+ defaultValue =
+ Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME.getId(),
+ defaultValue);
+ } else {
+ Preferences.savePreference(
+ FileManagerSettings.SETTINGS_EDITOR_SH_COLOR_SCHEME,
+ defaultValue,
+ true);
+ }
+ int[] colorScheme = toColorShemeArray(defaultValue);
+ HighlightColors[] colors = HighlightColors.values();
+ int cc = colors.length;
+ this.mColorScheme = new ColorPickerPreference[cc];
+ for (int i = 0; i < cc; i++) {
+ this.mColorScheme[i] = (ColorPickerPreference)findPreference(colors[i].getId());
+ setColorScheme(colors[i], colorScheme, i);
+ this.mColorScheme[i].setOnPreferenceChangeListener(this.mOnChangeListener);
+ }
+ } catch (Exception e) {
+ ExceptionUtil.translateException(getActivity(), e);
+ }
+ }
+
+ /**
+ * Method that set the enabled status of the color schemes preferences
+ *
+ * @param enable If the color scheme preferences should be enabled or not.
+ * @hide
+ */
+ void setColorSchemeEnabled(final boolean enable) {
+ int cc = this.mColorScheme.length;
+ for (int i = 0; i < cc; i++) {
+ this.mColorScheme[i].setEnabled(enable);
+ }
+ this.mResetColorScheme.setEnabled(enable);
+ }
+
+ /**
+ * Method that set a color scheme (use setting or theme default)
+ *
+ * @param color The color reference
+ * @param colorScheme The array of colors
+ * @param pos The position of the color
+ * @hide
+ */
+ void setColorScheme(HighlightColors color, int[] colorScheme, int pos) {
+ try {
+ this.mColorScheme[pos].setColor(colorScheme[pos]);
+ } catch (Exception e) {
+ this.mColorScheme[pos].setColor(
+ ThemeManager.getCurrentTheme(
+ getActivity()).getColor(getActivity(), color.getResId()));
+ Log.w(TAG,
+ String.format(
+ "Color scheme value not found for \"%s\"", //$NON-NLS-1$
+ color.getId()));
+ }
+ }
+
+ /**
+ * Method that returns if the preference is part of the color scheme preferences
+ *
+ * @return boolean Whether preference is part of the color scheme preferences
+ * @hide
+ */
+ static boolean isColorSchemePreference(final Preference preference) {
+ String key = preference.getKey();
+ if (key == null) {
+ return false;
+ }
+ HighlightColors[] colors = HighlightColors.values();
+ int cc = colors.length;
+ for (int i = 0; i < cc; i++) {
+ if (colors[i].getId().compareTo(key) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Method that converts the string set of color schemes to an array of colors
+ *
+ * @param value The string set of color schemes to parse
+ * @return int[] Array of colors
+ */
+ public static int[] toColorShemeArray(String value) {
+ if (value == null || value.length() == 0) {
+ return new int[]{};
+ }
+ String[] values = value.split("\\|"); //$NON-NLS-1$
+ int[] colors = new int[values.length];
+ int cc = colors.length;
+ for (int i = 0; i < cc; i++) {
+ try {
+ colors[i] = Integer.parseInt(values[i]);
+ } catch (Exception e) {
+ Log.w(TAG,
+ String.format(
+ "Problem parsing color value \"%s\" on position %d", //$NON-NLS-1$
+ values[i], Integer.valueOf(i)));
+ colors[i] = 0;
+ }
+ }
+ return colors;
+ }
+
+ /**
+ * Method that converts all the color scheme preference to one unified preference set
+ *
+ * @param preference The color scheme preference that was changed
+ * @param newValue The new value of the color scheme
+ * @return colorScheme The actual color schemes
+ * @hide
+ */
+ String toColorSchemeSet(final Preference preference, final int newValue) {
+ int cc = this.mColorScheme.length;
+ String[] colorSchemes = new String[cc];
+ for (int i = 0; i < cc; i++) {
+ String prop = String.valueOf(this.mColorScheme[i].getColor());
+ if (this.mColorScheme[i].getKey().compareTo(preference.getKey()) == 0) {
+ prop = String.valueOf(newValue);
+ }
+ colorSchemes[i] = prop;
+ }
+ return TextUtils.join("|", colorSchemes); //$NON-NLS-1$
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/GeneralPreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/GeneralPreferenceFragment.java
new file mode 100644
index 000000000..78dc52b24
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/GeneralPreferenceFragment.java
@@ -0,0 +1,235 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.util.Log;
+
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.console.ConsoleBuilder;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.ObjectStringIdentifier;
+import me.toolify.backbone.preferences.Preferences;
+
+/**
+ * A class that manages the commons options of the application
+ */
+public class GeneralPreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "GeneralPreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private CheckBoxPreference mCaseSensitiveSort;
+ private ListPreference mFiletimeFormatMode;
+ private ListPreference mFreeDiskSpaceWarningLevel;
+ private CheckBoxPreference mComputeFolderStatistics;
+ private CheckBoxPreference mDisplayThumbs;
+// private CheckBoxPreference mUseFlinger;
+ private ListPreference mAccessMode;
+ private CheckBoxPreference mDebugTraces;
+
+ /**
+ * @hide
+ */
+ boolean mLoaded = false;
+
+ private final OnPreferenceChangeListener mOnChangeListener =
+ new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(final Preference preference, Object newValue) {
+ boolean ret = true;
+
+ String key = preference.getKey();
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("New value for %s: %s", //$NON-NLS-1$
+ key,
+ String.valueOf(newValue)));
+ }
+
+ // Filetime format mode
+ if (FileManagerSettings.SETTINGS_FILETIME_FORMAT_MODE.
+ getId().compareTo(key) == 0) {
+ String value = (String)newValue;
+ int valueId = Integer.valueOf(value).intValue();
+ String[] labels = getResources().getStringArray(
+ R.array.filetime_format_mode_labels);
+ preference.setSummary(labels[valueId]);
+ }
+
+ // Disk usage warning level
+ else if (FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.
+ getId().compareTo(key) == 0) {
+ String value = (String)newValue;
+ preference.setSummary(
+ getResources().getString(
+ R.string.pref_disk_usage_warning_level_summary, value));
+ }
+
+ // Access mode
+ else if (FileManagerSettings.SETTINGS_ACCESS_MODE.getId().compareTo(key) == 0) {
+ Activity activity = GeneralPreferenceFragment.this.getActivity();
+
+ String value = (String)newValue;
+ AccessMode oldMode = FileManagerApplication.getAccessMode();
+ AccessMode newMode = AccessMode.fromId(value);
+ if (oldMode.compareTo(newMode) != 0) {
+ // The mode was changes. Change the console
+ if (newMode.compareTo(AccessMode.ROOT) == 0) {
+ if (!ConsoleBuilder.changeToPrivilegedConsole(
+ activity.getApplicationContext())) {
+ value = String.valueOf(oldMode.ordinal());
+ ret = false;
+ }
+ } else {
+ if (!ConsoleBuilder.changeToNonPrivilegedConsole(
+ activity.getApplicationContext())) {
+ value = String.valueOf(oldMode.ordinal());
+ ret = false;
+ }
+ }
+ }
+
+ int valueId = Integer.valueOf(value).intValue();
+ String[] summary = getResources().getStringArray(
+ R.array.access_mode_summaries);
+ preference.setSummary(summary[valueId]);
+ }
+
+ // Notify the change (only if fragment is loaded. Default values are loaded
+ // while not in loaded mode)
+ if (GeneralPreferenceFragment.this.mLoaded && ret) {
+ Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED);
+ intent.putExtra(
+ FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey());
+ getActivity().sendBroadcast(intent);
+ }
+
+ return ret;
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_general);
+
+ // Case sensitive sort
+ this.mCaseSensitiveSort =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_CASE_SENSITIVE_SORT.getId());
+ this.mCaseSensitiveSort.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Filetime format mode
+ this.mFiletimeFormatMode =
+ (ListPreference)findPreference(
+ FileManagerSettings.SETTINGS_FILETIME_FORMAT_MODE.getId());
+ String defaultValue = ((ObjectStringIdentifier)FileManagerSettings.
+ SETTINGS_FILETIME_FORMAT_MODE.getDefaultValue()).getId();
+ String value = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_FILETIME_FORMAT_MODE.getId(),
+ defaultValue);
+ this.mOnChangeListener.onPreferenceChange(this.mFiletimeFormatMode, value);
+ this.mFiletimeFormatMode.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Disk usage warning level
+ this.mFreeDiskSpaceWarningLevel =
+ (ListPreference)findPreference(
+ FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId());
+ defaultValue = ((String)FileManagerSettings.
+ SETTINGS_DISK_USAGE_WARNING_LEVEL.getDefaultValue());
+ value = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_DISK_USAGE_WARNING_LEVEL.getId(),
+ defaultValue);
+ this.mOnChangeListener.onPreferenceChange(this.mFreeDiskSpaceWarningLevel, value);
+ this.mFreeDiskSpaceWarningLevel.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Compute folder statistics
+ this.mComputeFolderStatistics =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_COMPUTE_FOLDER_STATISTICS.getId());
+ this.mComputeFolderStatistics.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Display thumbs
+ this.mDisplayThumbs =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId());
+ Boolean defaultBooleanValue = ((Boolean)FileManagerSettings.
+ SETTINGS_DISPLAY_THUMBS.getDefaultValue());
+ Boolean booleanValue = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
+ defaultBooleanValue);
+ this.mOnChangeListener.onPreferenceChange(this.mDisplayThumbs, booleanValue);
+ this.mDisplayThumbs.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Use flinger
+// this.mUseFlinger =
+// (CheckBoxPreference)findPreference(
+// FileManagerSettings.SETTINGS_USE_FLINGER.getId());
+// this.mUseFlinger.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Access mode
+ this.mAccessMode =
+ (ListPreference)findPreference(
+ FileManagerSettings.SETTINGS_ACCESS_MODE.getId());
+ this.mAccessMode.setOnPreferenceChangeListener(this.mOnChangeListener);
+ defaultValue = ((ObjectStringIdentifier)FileManagerSettings.
+ SETTINGS_ACCESS_MODE.getDefaultValue()).getId();
+ value = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_ACCESS_MODE.getId(),
+ defaultValue);
+ this.mOnChangeListener.onPreferenceChange(this.mAccessMode, value);
+ // If device is not rooted, this setting cannot be changed
+ this.mAccessMode.setEnabled(FileManagerApplication.isDeviceRooted());
+
+ // Capture Debug traces
+ this.mDebugTraces =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_SHOW_TRACES.getId());
+ this.mDebugTraces.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Loaded
+ this.mLoaded = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_general);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/LicenseActivity.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/LicenseActivity.java
new file mode 100644
index 000000000..d72c15f14
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/LicenseActivity.java
@@ -0,0 +1,247 @@
+package me.toolify.backbone.activities.preferences;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.*;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import me.toolify.backbone.R;
+import me.toolify.backbone.adapters.LicenseAdapter;
+import me.toolify.backbone.model.License;
+import me.toolify.backbone.model.License.LICENSE_TYPE;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.util.ExceptionUtil;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LicenseActivity extends Activity {
+
+ private static final String TAG = "LicenseActivity"; //$NON-NLS-1$
+
+ private static boolean DEBUG = false;
+
+ /**
+ * @hide
+ */
+ private List mLicenses;
+ /**
+ * @hide
+ */
+ private ListView mListView;
+ /**
+ * @hide
+ */
+ private ListAdapter mAdapter;
+ boolean mIsEmpty;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCreate(Bundle state) {
+ if (DEBUG) {
+ Log.d(TAG, "HistoryActivity.onCreate"); //$NON-NLS-1$
+ }
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+
+ //Set in transition
+ overridePendingTransition(R.anim.translate_to_right_in, R.anim.hold_out);
+
+ //Set the main layout of the activity
+ setContentView(R.layout.licenses);
+
+ //Initialize action bars and data
+ initTitleActionBar();
+ initLicenses();
+
+ // Apply the theme
+ applyTheme();
+
+ //Save state
+ super.onCreate(state);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "HistoryActivity.onDestroy"); //$NON-NLS-1$
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onPause() {
+ //Set out transition
+ overridePendingTransition(R.anim.hold_in, R.anim.translate_to_left_out);
+ super.onPause();
+ }
+
+ /**
+ * Method that initializes the titlebar of the activity.
+ */
+ private void initTitleActionBar() {
+ //Configure the action bar options
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setTitle(R.string.licenses);
+ }
+
+ /**
+ * Method that initializes the licenses listview of the activity.
+ */
+ private void initLicenses() {
+ this.mListView = (ListView)findViewById(R.id.licenses_listview);
+ mLicenses = new ArrayList();
+ final LicenseAdapter mAdapter = new LicenseAdapter(this, mLicenses);
+ this.mListView.setAdapter(mAdapter);
+
+ // Retrieve the loading view
+ final View waiting = findViewById(R.id.licenses_waiting);
+
+ this.mListView = (ListView)findViewById(R.id.licenses_listview);
+
+ // Load the history in background
+ AsyncTask task = new AsyncTask() {
+ Exception mCause;
+ List mLicenses;
+
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ try {
+ this.mLicenses = loadLicenses();
+ if (this.mLicenses.isEmpty()) {
+ View msg = findViewById(R.id.licenses_empty_msg);
+ msg.setVisibility(View.VISIBLE);
+ return Boolean.TRUE;
+ }
+ LicenseActivity.this.mIsEmpty = this.mLicenses.isEmpty();
+
+ //Show inverted history
+ final List adapterList = new ArrayList(this.mLicenses);
+ LicenseActivity.this.mAdapter =
+ new LicenseAdapter(LicenseActivity.this, adapterList);
+
+ return Boolean.TRUE;
+
+ } catch (Exception e) {
+ this.mCause = e;
+ return Boolean.FALSE;
+ }
+ }
+
+ @Override
+ protected void onPreExecute() {
+ waiting.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ waiting.setVisibility(View.GONE);
+ if (result.booleanValue()) {
+ if (LicenseActivity.this.mListView != null &&
+ LicenseActivity.this.mAdapter != null) {
+
+ LicenseActivity.this.mListView.
+ setAdapter(LicenseActivity.this.mAdapter);
+ }
+
+ } else {
+ if (this.mCause != null) {
+ ExceptionUtil.translateException(LicenseActivity.this, this.mCause);
+ }
+ }
+ }
+
+ @Override
+ protected void onCancelled() {
+ waiting.setVisibility(View.GONE);
+ }
+ };
+ task.execute();
+ }
+
+ private List loadLicenses() {
+ String[] mHeaderArray = getResources().getStringArray(R.array.credits_headers);
+ String[] mDetailArray = getResources().getStringArray(R.array.credits_details);
+ List list = new ArrayList();
+
+ int len = mHeaderArray.length;
+ for (int i = 0; i < len; i++) {
+ License l = new License(LICENSE_TYPE.LICENSE, mHeaderArray[i], mDetailArray[i]);
+ list.add(l);
+
+ }
+ return list;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ back();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ back();
+ return true;
+ default:
+ return super.onKeyUp(keyCode, event);
+ }
+ }
+
+
+ /**
+ * Method that returns to previous activity and.
+ */
+ private void back() {
+ Intent intent = new Intent();
+ setResult(RESULT_CANCELED, intent);
+ finish();
+ }
+
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ ThemeManager.Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, false);
+
+ // -View
+ theme.setBackgroundDrawable(this, getWindow().getDecorView(), "background_drawable"); //$NON-NLS-1$
+ this.mListView.setDivider(
+ theme.getDrawable(this, "horizontal_divider_drawable")); //$NON-NLS-1$
+ this.mListView.invalidate();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SearchPreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SearchPreferenceFragment.java
new file mode 100644
index 000000000..85f1f8ba7
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SearchPreferenceFragment.java
@@ -0,0 +1,196 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.provider.SearchRecentSuggestions;
+import android.util.Log;
+import android.widget.Toast;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.ObjectStringIdentifier;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.providers.RecentSearchesContentProvider;
+import me.toolify.backbone.util.DialogHelper;
+
+/**
+ * A class that manages the search options
+ */
+public class SearchPreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "SearchPreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ // Internal keys
+ private static final String REMOVE_SEARCH_TERMS_KEY =
+ "cm_filemanager_remove_saved_search_terms"; //$NON-NLS-1$
+
+ private CheckBoxPreference mHighlightTerms;
+ private CheckBoxPreference mShowRelevanceWidget;
+ private ListPreference mSortSearchResultMode;
+ private CheckBoxPreference mSaveSearchTerms;
+ private Preference mRemoveSearchTerms;
+
+ /**
+ * @hide
+ */
+ boolean mLoaded = false;
+
+ private final OnPreferenceChangeListener mOnChangeListener =
+ new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String key = preference.getKey();
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("New value for %s: %s", //$NON-NLS-1$
+ key,
+ String.valueOf(newValue)));
+ }
+
+ // Saved search terms
+ if (preference.getKey().compareTo(
+ FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId()) == 0) {
+ if (!((Boolean)newValue).booleanValue()) {
+ // Remove search terms if saved search terms
+ // is not active by the user
+ clearRecentSearchTerms();
+ }
+
+ // Sort search result mode
+ } else if (FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.
+ getId().compareTo(key) == 0) {
+ int value = Integer.valueOf((String)newValue).intValue();
+ String[] summary = getResources().getStringArray(
+ R.array.sort_search_results_mode_labels);
+ preference.setSummary(summary[value]);
+ }
+
+ // Notify the change (only if fragment is loaded. Default values are loaded
+ // while not in loaded mode)
+ if (SearchPreferenceFragment.this.mLoaded) {
+ Intent intent = new Intent(FileManagerSettings.INTENT_SETTING_CHANGED);
+ intent.putExtra(
+ FileManagerSettings.EXTRA_SETTING_CHANGED_KEY, preference.getKey());
+ getActivity().sendBroadcast(intent);
+ }
+
+ return true;
+ }
+ };
+
+ private final OnPreferenceClickListener mOnClickListener =
+ new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (preference.getKey().compareTo(REMOVE_SEARCH_TERMS_KEY) == 0) {
+ // Remove search terms
+ clearRecentSearchTerms();
+
+ // Advise the user
+ DialogHelper.showToast(
+ getActivity(),
+ getActivity().getString(R.string.pref_remove_saved_search_terms_msg),
+ Toast.LENGTH_SHORT);
+ }
+ return false;
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+ this.mLoaded = false;
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_search);
+
+ // Highlight terms
+ this.mHighlightTerms =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_HIGHLIGHT_TERMS.getId());
+ this.mHighlightTerms.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Relevance widget
+ this.mShowRelevanceWidget =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_SHOW_RELEVANCE_WIDGET.getId());
+ this.mShowRelevanceWidget.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Sort search result mode
+ this.mSortSearchResultMode =
+ (ListPreference)findPreference(
+ FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId());
+ this.mSortSearchResultMode.setOnPreferenceChangeListener(this.mOnChangeListener);
+ String defaultValue = ((ObjectStringIdentifier)FileManagerSettings.
+ SETTINGS_SORT_SEARCH_RESULTS_MODE.getDefaultValue()).getId();
+ String value = Preferences.getSharedPreferences().getString(
+ FileManagerSettings.SETTINGS_SORT_SEARCH_RESULTS_MODE.getId(),
+ defaultValue);
+ this.mOnChangeListener.onPreferenceChange(this.mSortSearchResultMode, value);
+
+ // Saved search terms
+ this.mSaveSearchTerms =
+ (CheckBoxPreference)findPreference(
+ FileManagerSettings.SETTINGS_SAVE_SEARCH_TERMS.getId());
+ this.mSaveSearchTerms.setOnPreferenceChangeListener(this.mOnChangeListener);
+
+ // Remove search terms
+ this.mRemoveSearchTerms = findPreference(REMOVE_SEARCH_TERMS_KEY);
+ this.mRemoveSearchTerms.setOnPreferenceClickListener(this.mOnClickListener);
+
+ // Loaded
+ this.mLoaded = true;
+ }
+
+ /**
+ * Method that removes the recent suggestions on search activity
+ * @hide
+ */
+ void clearRecentSearchTerms() {
+ SearchRecentSuggestions suggestions =
+ new SearchRecentSuggestions(getActivity(),
+ RecentSearchesContentProvider.AUTHORITY,
+ RecentSearchesContentProvider.MODE);
+ suggestions.clearHistory();
+ Preferences.setLastSearch(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_search);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SettingsPreferences.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SettingsPreferences.java
new file mode 100644
index 000000000..528799e0e
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/SettingsPreferences.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.app.Fragment;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.util.Log;
+import android.view.MenuItem;
+
+import java.util.List;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.dashclock.DashExtension;
+import me.toolify.backbone.dashclock.DashSettings;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.util.AndroidHelper;
+
+/**
+ * The {@link SettingsPreferences} preferences
+ */
+public class SettingsPreferences extends PreferenceActivity {
+
+ private static final String TAG = "SettingsPreferences"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private final BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (intent.getAction().compareTo(FileManagerSettings.INTENT_THEME_CHANGED) == 0) {
+ finish();
+ }
+ }
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (DEBUG) {
+ Log.d(TAG, "SettingsPreferences.onCreate"); //$NON-NLS-1$
+ }
+
+ // Register the broadcast receiver
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(FileManagerSettings.INTENT_THEME_CHANGED);
+ registerReceiver(this.mNotificationReceiver, filter);
+
+ //Initialize action bars
+ initTitleActionBar();
+
+ // Apply the theme
+ applyTheme();
+
+ super.onCreate(savedInstanceState);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) {
+ Log.d(TAG, "SettingsPreferences.onDestroy"); //$NON-NLS-1$
+ }
+
+ // Unregister the receiver
+ try {
+ unregisterReceiver(this.mNotificationReceiver);
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+
+ //All destroy. Continue
+ super.onDestroy();
+ }
+
+ /**
+ * Method that initializes the titlebar of the activity.
+ */
+ private void initTitleActionBar() {
+ //Configure the action bar options
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getActionBar().setTitle(R.string.pref);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onBuildHeaders(List target) {
+ loadHeadersFromResource(R.xml.preferences_headers, target);
+
+ /* Manually load the header icons, since Android chokes on icons referenced via '?attr/xxx'
+ which must be done for theming */
+ final TypedArray a = getTheme().obtainStyledAttributes(R.styleable.FileManagerPrefs);
+
+ for (Header header : target) {
+ switch (header.titleRes) {
+ case R.string.pref_general:
+ header.iconRes = a.getResourceId(R.styleable.FileManagerPrefs_preferenceIconGeneral,
+ R.drawable.ic_preference_black_general);
+ break;
+ case R.string.pref_search:
+ header.iconRes = a.getResourceId(R.styleable.FileManagerPrefs_preferenceIconSearch,
+ R.drawable.ic_preference_black_search);
+ break;
+ case R.string.pref_editor:
+ header.iconRes = a.getResourceId(R.styleable.FileManagerPrefs_preferenceIconEditor,
+ R.drawable.ic_preference_black_editor);
+ break;
+ case R.string.pref_about:
+ header.iconRes = a.getResourceId(R.styleable.FileManagerPrefs_preferenceIconAbout,
+ R.drawable.ic_preference_black_about);
+ break;
+ }
+ }
+
+ // Create Dashclock preference if the user has enabled our Dashclock extension
+ if(DashExtension.isEnabled(this)) {
+ Header dashHeader = new Header();
+ dashHeader.titleRes = R.string.pref_dashclock;
+ Intent i = new Intent(this, DashSettings.class);
+ i.putExtra("showLauncherIcon", true);
+ dashHeader.intent = i;
+ dashHeader.iconRes = a.getResourceId(R.styleable.FileManagerPrefs_preferenceIconDashclock,
+ R.drawable.ic_preference_black_dashclock);
+ target.add(dashHeader);
+ }
+
+ a.recycle();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ invalidateHeaders();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ super.onAttachFragment(fragment);
+ if (!AndroidHelper.isTablet(this) && fragment instanceof TitlePreferenceFragment) {
+ getActionBar().setTitle(((TitlePreferenceFragment)fragment).getTitle());
+ } else {
+ getActionBar().setTitle(R.string.pref);
+ }
+ }
+
+ /**
+ * Method that applies the current theme to the activity
+ * @hide
+ */
+ void applyTheme() {
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, false);
+
+ // -View
+ theme.setBackgroundDrawable(
+ this,
+ this.getWindow().getDecorView(),
+ "background_drawable"); //$NON-NLS-1$
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/ThemesPreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/ThemesPreferenceFragment.java
new file mode 100644
index 000000000..0aeec6f21
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/ThemesPreferenceFragment.java
@@ -0,0 +1,96 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.util.Log;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.preferences.ThemeSelectorPreference;
+
+/**
+ * A class that manages the theme selection
+ */
+public class ThemesPreferenceFragment extends TitlePreferenceFragment {
+
+ private static final String TAG = "ThemesPreferenceFragment"; //$NON-NLS-1$
+
+ private static final boolean DEBUG = false;
+
+ private ThemeSelectorPreference mThemeSelector;
+
+ private final OnPreferenceChangeListener mOnChangeListener =
+ new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String key = preference.getKey();
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("New value for %s: %s", //$NON-NLS-1$
+ key,
+ String.valueOf(newValue)));
+ }
+
+ // Notify to all activities that the theme has changed
+ Intent intent = new Intent(FileManagerSettings.INTENT_THEME_CHANGED);
+ intent.putExtra(FileManagerSettings.EXTRA_THEME_ID, (String)newValue);
+ getActivity().sendBroadcast(intent);
+
+ //Wait for allow activities to apply the theme, prior to finish settings
+ try {
+ Thread.sleep(250L);
+ } catch (Throwable e) {/**NON BLOCK**/}
+ getActivity().finish();
+ return true;
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Change the preference manager
+ getPreferenceManager().setSharedPreferencesName(Preferences.SETTINGS_FILENAME);
+ getPreferenceManager().setSharedPreferencesMode(Context.MODE_PRIVATE);
+
+ // Add the preferences
+ addPreferencesFromResource(R.xml.preferences_themes);
+
+ // Theme selector
+ this.mThemeSelector =
+ (ThemeSelectorPreference)findPreference(
+ FileManagerSettings.SETTINGS_THEME.getId());
+ this.mThemeSelector.setOnPreferenceChangeListener(this.mOnChangeListener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getTitle() {
+ return getString(R.string.pref_themes);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/activities/preferences/TitlePreferenceFragment.java b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/TitlePreferenceFragment.java
new file mode 100644
index 000000000..f49589e8a
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/activities/preferences/TitlePreferenceFragment.java
@@ -0,0 +1,31 @@
+/*
+ * 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 me.toolify.backbone.activities.preferences;
+
+import android.preference.PreferenceFragment;
+
+/**
+ * The base class of all preference fragments of the backbone app
+ */
+public abstract class TitlePreferenceFragment extends PreferenceFragment {
+ /**
+ * Method that returns the title of the preference fragment
+ *
+ * @return CharSequence The title of the fragment
+ */
+ public abstract CharSequence getTitle();
+}
diff --git a/src/com/cyanogenmod/filemanager/adapters/AssociationsAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/AssociationsAdapter.java
similarity index 91%
rename from src/com/cyanogenmod/filemanager/adapters/AssociationsAdapter.java
rename to Backbone/src/main/java/me/toolify/backbone/adapters/AssociationsAdapter.java
index 22f6c3c95..9d156396c 100644
--- a/src/com/cyanogenmod/filemanager/adapters/AssociationsAdapter.java
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/AssociationsAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.adapters;
+package me.toolify.backbone.adapters;
import android.content.Context;
import android.content.pm.ResolveInfo;
@@ -22,14 +22,15 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import me.toolify.backbone.R;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
import java.util.List;
@@ -72,6 +73,7 @@ public DataHolder() {
private DataHolder[] mData;
+ private AdapterView> mParent;
private final OnItemClickListener mOnItemClickListener;
//The resource item layout
@@ -86,13 +88,16 @@ public DataHolder() {
* Constructor of AssociationsAdapter.
*
* @param context The current context
+ * @param parent The adapter view
* @param intents The intents info
* @param onItemClickListener The listener for listen action clicks
*/
public AssociationsAdapter(
- Context context, List intents, OnItemClickListener onItemClickListener) {
+ Context context, AdapterView> parent,
+ List intents, OnItemClickListener onItemClickListener) {
super(context, RESOURCE_ITEM_NAME, intents);
this.mOnItemClickListener = onItemClickListener;
+ this.mParent = parent;
//Do cache of the data for better performance
processData(intents);
@@ -182,7 +187,7 @@ public View getView(int position, View convertView, ViewGroup parent) {
@Override
public void onClick(View v) {
ViewHolder viewHolder = (ViewHolder)v.getTag();
- this.mOnItemClickListener.onItemClick(null, v, viewHolder.mPosition, v.getId());
+ this.mOnItemClickListener.onItemClick(this.mParent, v, viewHolder.mPosition, v.getId());
}
}
diff --git a/Backbone/src/main/java/me/toolify/backbone/adapters/BookmarksAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/BookmarksAdapter.java
new file mode 100644
index 000000000..1268ec3df
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/BookmarksAdapter.java
@@ -0,0 +1,294 @@
+/*
+ * 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 me.toolify.backbone.adapters;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.model.Bookmark;
+import me.toolify.backbone.model.Bookmark.BOOKMARK_TYPE;
+import me.toolify.backbone.ui.IconHolder;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.util.BookmarksHelper;
+
+import java.util.List;
+
+/**
+ * An implementation of {@link ArrayAdapter} for display bookmarks.
+ */
+public class BookmarksAdapter extends ArrayAdapter {
+
+ /**
+ * A class that conforms with the ViewHolder pattern to performance
+ * the list view rendering.
+ */
+ private static class ViewHolder {
+ /**
+ * @hide
+ */
+ public ViewHolder() {
+ super();
+ }
+ TextView mTvSeparator;
+ ImageView mTvIcon;
+ TextView mTvName;
+ TextView mTvPath;
+ ImageButton mBtAction;
+ }
+
+ /**
+ * A class that holds the full data information.
+ */
+ private static class DataHolder {
+ /**
+ * @hide
+ */
+ public DataHolder() {
+ super();
+ }
+ Drawable mDwIcon;
+ String mName;
+ String mPath;
+ String mCategoryTitle;
+ Drawable mDwAction;
+ String mActionCd;
+ }
+
+
+
+ private DataHolder[] mData;
+ private int[] mCellStates;
+ private IconHolder mIconHolder;
+ private final OnClickListener mOnActionClickListener;
+
+ // The resource item layout
+ private static final int RESOURCE_LAYOUT = R.layout.bookmarks_item;
+
+ // The resource of the item separator
+ private static final int RESOURCE_ITEM_SEPARATOR = R.id.bookmarks_item_separator;
+ // The resource of the item icon
+ private static final int RESOURCE_ITEM_ICON = R.id.bookmarks_item_icon;
+ // The resource of the item name
+ private static final int RESOURCE_ITEM_NAME = R.id.bookmarks_item_name;
+ // The resource of the item directory
+ private static final int RESOURCE_ITEM_PATH = R.id.bookmarks_item_path;
+ // The resource of the item button action
+ private static final int RESOURCE_ITEM_ACTION = R.id.bookmarks_item_action;
+
+ // State of ListView item that has never been determined.
+ private static final int STATE_UNKNOWN = 0;
+ //State of a ListView item that is sectioned. A sectioned item must display the separator.
+ private static final int STATE_SECTIONED_CELL = 1;
+ //State of a ListView item that is not sectioned and therefore does not display the separator.
+ private static final int STATE_REGULAR_CELL = 2;
+
+ /**
+ * Constructor of BookmarksAdapter.
+ *
+ * @param context The current context
+ * @param bookmarks The bookmarks
+ * @param onActionClickListener The listener for listen action clicks
+ */
+ public BookmarksAdapter(
+ Context context, List bookmarks, OnClickListener onActionClickListener) {
+ super(context, RESOURCE_ITEM_NAME, bookmarks);
+ this.mIconHolder = new IconHolder(context);
+ this.mOnActionClickListener = onActionClickListener;
+
+ //Do cache of the data for better performance
+ processData(bookmarks);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ processData(null);
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Method that dispose the elements of the adapter.
+ */
+ public void dispose() {
+ clear();
+ this.mData = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
+ }
+
+ /**
+ * Method that process the data before use {@link #getView} method.
+ *
+ * @param bookmarks The list of bookmarks (to better performance) or null.
+ */
+ private void processData(List bookmarks) {
+ this.mData = new DataHolder[getCount()];
+ this.mCellStates = new int[getCount()];
+ int cc = (bookmarks == null) ? getCount() : bookmarks.size();
+ for (int i = 0; i < cc; i++) {
+ //Bookmark info
+ Bookmark bookmark = (bookmarks == null) ? getItem(i) : bookmarks.get(i);
+
+ //Build the data holder
+ this.mData[i] = new BookmarksAdapter.DataHolder();
+ this.mData[i].mDwIcon =
+ this.mIconHolder.getDrawable(BookmarksHelper.getIcon(bookmark));
+ this.mData[i].mName = bookmark.mName;
+ this.mData[i].mPath = bookmark.mPath;
+ switch (bookmark.mCategory) {
+ case LOCATIONS:
+ this.mData[i].mCategoryTitle = getContext().getString(R.string.bookmarks_header_locations);
+ break;
+ case USER_BOOKMARKS:
+ this.mData[i].mCategoryTitle = getContext().getString(R.string.bookmarks_header_user_bookmarks);
+ break;
+ case CLOUD:
+ this.mData[i].mCategoryTitle = getContext().getString(R.string.bookmarks_header_cloud);
+ break;
+ }
+ this.mData[i].mDwAction = null;
+ this.mData[i].mActionCd = null;
+ if (bookmark.mType.compareTo(BOOKMARK_TYPE.HOME) == 0) {
+ this.mData[i].mDwAction =
+ this.mIconHolder.getDrawable("ic_config_drawable"); //$NON-NLS-1$
+ this.mData[i].mActionCd =
+ getContext().getString(R.string.bookmarks_button_config_cd);
+ } else if (bookmark.mType.compareTo(BOOKMARK_TYPE.USER_DEFINED) == 0) {
+ this.mData[i].mDwAction =
+ this.mIconHolder.getDrawable("ic_close_drawable"); //$NON-NLS-1$
+ this.mData[i].mActionCd =
+ getContext().getString(R.string.bookmarks_button_remove_bookmark_cd);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ // Separator List View Header
+ boolean needSeparator = false;
+ switch (mCellStates[position]) {
+ case STATE_SECTIONED_CELL:
+ needSeparator = true;
+ break;
+
+ case STATE_REGULAR_CELL:
+ needSeparator = false;
+ break;
+
+ case STATE_UNKNOWN:
+ default:
+ // A separator is needed if it's the first itemview of the
+ // ListView or if the group of the current cell is different
+ // from the previous itemview.
+ if (position == 0) {
+ needSeparator = true;
+ } else {
+ // Test to see if the category has changed since the last bookmark
+ if (getItem(position).mCategory != null && getItem(position-1).mCategory != null &&
+ getItem(position).mCategory != getItem(position-1).mCategory) {
+ needSeparator = true;
+ }
+ }
+
+ // Cache the result
+ mCellStates[position] = needSeparator ? STATE_SECTIONED_CELL : STATE_REGULAR_CELL;
+ break;
+ }
+
+ //Check to reuse view
+ View v = convertView;
+ if (v == null) {
+ //Create the view holder
+ LayoutInflater li =
+ (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ v = li.inflate(RESOURCE_LAYOUT, parent, false);
+ ViewHolder viewHolder = new BookmarksAdapter.ViewHolder();
+ viewHolder.mTvSeparator = (TextView)v.findViewById(RESOURCE_ITEM_SEPARATOR);
+ viewHolder.mTvIcon = (ImageView)v.findViewById(RESOURCE_ITEM_ICON);
+ viewHolder.mTvName = (TextView)v.findViewById(RESOURCE_ITEM_NAME);
+ viewHolder.mTvPath = (TextView)v.findViewById(RESOURCE_ITEM_PATH);
+ viewHolder.mBtAction = (ImageButton)v.findViewById(RESOURCE_ITEM_ACTION);
+ viewHolder.mBtAction.setTag(Integer.valueOf(position));
+ v.setTag(viewHolder);
+
+ // Apply the current theme
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+ theme.setBackgroundDrawable(
+ getContext(), v, "selectors_deselected_drawable"); //$NON-NLS-1$
+/* Legacy CM theme code. We should be getting this stuff exclusively
+ from the layout, because this is a nightmare to keep orderly.
+ theme.setTextColor(
+ getContext(), viewHolder.mTvName, "nav_drawer_text_color"); //$NON-NLS-1$
+ theme.setTextColor(
+ getContext(), viewHolder.mTvPath, "nav_drawer_text_color"); //$NON-NLS-1$*/
+ }
+
+ //Retrieve data holder
+ final DataHolder dataHolder = this.mData[position];
+
+ //Retrieve the view holder
+ ViewHolder viewHolder = (ViewHolder)v.getTag();
+
+ //Set the data
+ if (needSeparator) {
+ viewHolder.mTvSeparator.setText(dataHolder.mCategoryTitle);
+ viewHolder.mTvSeparator.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.mTvSeparator.setVisibility(View.GONE);
+ }
+ viewHolder.mTvIcon.setImageDrawable(dataHolder.mDwIcon);
+ viewHolder.mTvName.setText(dataHolder.mName);
+ viewHolder.mTvPath.setText(dataHolder.mPath);
+ boolean hasAction = dataHolder.mDwAction != null;
+ viewHolder.mBtAction.setImageDrawable(hasAction ? dataHolder.mDwAction : null);
+ viewHolder.mBtAction.setVisibility(hasAction ? View.VISIBLE : View.GONE);
+ viewHolder.mBtAction.setOnClickListener(this.mOnActionClickListener);
+ viewHolder.mBtAction.setContentDescription(dataHolder.mActionCd);
+
+ //Return the view
+ return v;
+ }
+
+ /**
+ * Method that should be invoked when the theme of the app was changed
+ */
+ public void notifyThemeChanged() {
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ }
+ // Empty icon holder
+ this.mIconHolder = new IconHolder(getContext());
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/adapters/BreadcrumbSpinnerAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/BreadcrumbSpinnerAdapter.java
new file mode 100644
index 000000000..fc8f82a2d
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/BreadcrumbSpinnerAdapter.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.adapters;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.RelativeLayout;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.util.FileHelper;
+
+public class BreadcrumbSpinnerAdapter extends BaseAdapter implements SpinnerAdapter{
+
+ private Context mContext;
+ private ArrayList fileList;
+
+ public BreadcrumbSpinnerAdapter(Context context, ArrayList fileList) {
+ this.mContext = context;
+ this.fileList = fileList;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getCount() {
+ return fileList.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getItem(int position) {
+ return fileList.get(position);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ RelativeLayout row = (RelativeLayout) View.inflate(mContext, R.layout.breadcrumb_spinner_selected_item, null);
+ TextView title = (TextView)row.findViewById(R.id.breadcrumb_spinner_item_title);
+ TextView subtitle = (TextView)row.findViewById(R.id.breadcrumb_spinner_item_subtitle);
+ title.setText(buildTitleString(fileList.get(position)));
+ subtitle.setText(buildSubtitleString(fileList.get(position)));
+ return row;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ TextView textView = (TextView) View.inflate(mContext, R.layout.breadcrumb_spinner_dropdown_item, null);
+ textView.setText(buildTitleString(fileList.get(position)));
+ return textView;
+ }
+
+ private String buildTitleString(File file) {
+ String fileString;
+ if (file.compareTo(new File(FileHelper.ROOT_DIRECTORY)) == 0){
+ fileString = "/";
+ } else {
+ fileString = file.getName();
+ }
+ return fileString;
+ }
+
+ private String buildSubtitleString(File file) {
+ String fileString;
+ if (file.compareTo(new File(FileHelper.ROOT_DIRECTORY)) == 0){
+ fileString = "Filesystem Root";
+ } else {
+ fileString = file.getParent();
+ }
+ return fileString;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/adapters/CheckableListAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/CheckableListAdapter.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/adapters/CheckableListAdapter.java
rename to Backbone/src/main/java/me/toolify/backbone/adapters/CheckableListAdapter.java
index be28711f2..5c3ea0d87 100644
--- a/src/com/cyanogenmod/filemanager/adapters/CheckableListAdapter.java
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/CheckableListAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.adapters;
+package me.toolify.backbone.adapters;
import android.content.Context;
import android.view.LayoutInflater;
@@ -24,9 +24,9 @@
import android.widget.ImageView;
import android.widget.TextView;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import me.toolify.backbone.R;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
import java.util.List;
diff --git a/Backbone/src/main/java/me/toolify/backbone/adapters/FileSystemObjectAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/FileSystemObjectAdapter.java
new file mode 100644
index 000000000..3b2ac60ba
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/FileSystemObjectAdapter.java
@@ -0,0 +1,574 @@
+/*
+ * 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 me.toolify.backbone.adapters;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.greenrobot.event.EventBus;
+import me.toolify.backbone.R;
+import me.toolify.backbone.bus.events.OpenPropertiesDrawerEvent;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.ParentDirectory;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.IconHolder;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.image.ImageFetcher;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.MimeTypeHelper.MimeTypeCategory;
+
+/**
+ * An implementation of {@link ArrayAdapter} for display file system objects.
+ */
+public class FileSystemObjectAdapter
+ extends ArrayAdapter implements OnClickListener {
+
+ /**
+ * An interface to communicate selection changes events.
+ */
+ public interface OnSelectionChangedListener {
+ /**
+ * Method invoked when the selection changed.
+ *
+ * @param selectedItems The new selected items
+ */
+ void onSelectionChanged(List selectedItems);
+ }
+
+ /**
+ * A class that conforms with the ViewHolder pattern to performance
+ * the list view rendering.
+ */
+ private static class ViewHolder {
+ /**
+ * @hide
+ */
+ public ViewHolder() {
+ super();
+ }
+ ImageButton mBtInfo;
+ ImageView mBtIcon;
+ TextView mTvName;
+ TextView mTvSummary;
+ TextView mTvSize;
+ Boolean mHasSelectedBg;
+ }
+
+ /**
+ * A class that holds the full data information.
+ */
+ private static class DataHolder {
+ /**
+ * @hide
+ */
+ public DataHolder() {
+ super();
+ }
+ boolean mSelected;
+ Drawable mDwCheck;
+ Drawable mDwIcon;
+ String mName;
+ String mSummary;
+ String mSize;
+ String mImagePath;
+ boolean mDynamic;
+ }
+
+
+ private DataHolder[] mData;
+ private IconHolder mIconHolder;
+ private final int mItemViewResourceId;
+ private List mSelectedItems;
+ private final boolean mPickable;
+ private ImageFetcher mImageFetcher;
+ private boolean displayThumbs;
+
+ private OnSelectionChangedListener mOnSelectionChangedListener;
+
+ private boolean mDisposed;
+
+ //The resource of the item info button
+ private static final int RESOURCE_ITEM_INFO = R.id.navigation_view_item_info;
+ //The resource of the item icon
+ private static final int RESOURCE_ITEM_ICON = R.id.navigation_view_item_icon;
+ //The resource of the item name
+ private static final int RESOURCE_ITEM_NAME = R.id.navigation_view_item_name;
+ //The resource of the item summary information
+ private static final int RESOURCE_ITEM_SUMMARY = R.id.navigation_view_item_summary;
+ //The resource of the item size information
+ private static final int RESOURCE_ITEM_SIZE = R.id.navigation_view_item_size;
+
+ /**
+ * Constructor of FileSystemObjectAdapter.
+ *
+ * @param context The current context
+ * @param files The list of file system objects
+ * @param itemViewResourceId The identifier of the layout that represents an item
+ * of the list adapter
+ * @param pickable If the adapter should act as a pickable browser.
+ */
+ public FileSystemObjectAdapter(
+ Context context, List files,
+ int itemViewResourceId, boolean pickable, ImageFetcher imageFetcher) {
+ super(context, RESOURCE_ITEM_NAME, files);
+ this.mDisposed = false;
+ this.mItemViewResourceId = itemViewResourceId;
+ this.mSelectedItems = new ArrayList();
+ this.mPickable = pickable;
+ this.mImageFetcher = imageFetcher;
+ notifyThemeChanged(); // Reload icons
+
+ processData();
+ }
+
+ /**
+ * Method that sets the listener which communicates selection changes.
+ *
+ * @param onSelectionChangedListener The listener reference
+ */
+ public void setOnSelectionChangedListener(
+ OnSelectionChangedListener onSelectionChangedListener) {
+ this.mOnSelectionChangedListener = onSelectionChangedListener;
+ }
+
+ /**
+ * Method that loads the default icons (known icons and more common icons).
+ */
+ private void loadDefaultIcons() {
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ if (this.mDisposed) {
+ return;
+ }
+ processData();
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Method that dispose the elements of the adapter.
+ */
+ public void dispose() {
+ this.mDisposed = true;
+ clear();
+ this.mData = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
+ this.mSelectedItems.clear();
+ }
+
+ /**
+ * Method that returns the {@link FileSystemObject} reference from his path.
+ *
+ * @param path The path of the file system object
+ * @return FileSystemObject The file system object reference
+ */
+ public FileSystemObject getItem(String path) {
+ int cc = getCount();
+ for (int i = 0; i < cc; i++) {
+ //File system object info
+ FileSystemObject fso = getItem(i);
+ if (fso.getFullPath().compareTo(path) == 0) {
+ return fso;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Method that process the data before use {@link #getView} method.
+ */
+ private void processData() {
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+ Resources res = getContext().getResources();
+ int cc = getCount();
+
+ this.mData = new DataHolder[cc];
+
+ for (int i = 0; i < cc; i++) {
+ //File system object info
+ FileSystemObject fso = getItem(i);
+
+ //Parse the last modification time and permissions
+ StringBuilder sbSummary = new StringBuilder();
+ if (fso instanceof ParentDirectory) {
+ sbSummary.append(res.getString(R.string.parent_dir));
+ } else {
+ sbSummary.append(
+ FileHelper.formatFileTime(
+ getContext(), fso.getLastModifiedTime()));
+ sbSummary.append(" "); //$NON-NLS-1$
+ sbSummary.append(fso.toRawPermissionString());
+ }
+
+ //Build the data holder
+ this.mData[i] = new FileSystemObjectAdapter.DataHolder();
+ this.mData[i].mSelected = this.mSelectedItems.contains(fso);
+ this.mData[i].mDynamic = MimeTypeHelper.getIsDynamic(getContext(), fso);
+ this.mData[i].mImagePath = null;
+ if (this.mData[i].mDynamic) {
+ // Produce specific icon for file (e.g. apk or image thumbnail) and store it
+ if (FileHelper.getExtension(fso).equals("apk")) {
+ this.mData[i].mImagePath = fso.getFullPath();
+ } else if (MimeTypeHelper.getCategory(getContext(), fso) == MimeTypeCategory.IMAGE) {
+ // Gather image file path for lazy loading
+ this.mData[i].mImagePath = fso.getFullPath();
+ } else {
+ // Icon is marked as dynamic in mimetypes.properties but wasn't handled above
+ this.mData[i].mDwIcon = this.mIconHolder.getDrawable("ic_holo_dark_fs_warning");
+ }
+ } else {
+ // Display icon according to mimetype
+ this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
+ MimeTypeHelper.getIcon(getContext(), fso));
+ }
+ this.mData[i].mName = fso.getName();
+ this.mData[i].mSummary = sbSummary.toString();
+ this.mData[i].mSize = FileHelper.getHumanReadableSize(fso);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ //Check to reuse view
+ View v = convertView;
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+
+ if (v == null) {
+ //Create the view holder
+ LayoutInflater li =
+ (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ v = li.inflate(this.mItemViewResourceId, parent, false);
+ ViewHolder viewHolder = new FileSystemObjectAdapter.ViewHolder();
+ viewHolder.mBtIcon = (ImageButton)v.findViewById(RESOURCE_ITEM_ICON);
+ viewHolder.mTvName = (TextView)v.findViewById(RESOURCE_ITEM_NAME);
+ viewHolder.mTvSummary = (TextView)v.findViewById(RESOURCE_ITEM_SUMMARY);
+ viewHolder.mTvSize = (TextView)v.findViewById(RESOURCE_ITEM_SIZE);
+ if (!this.mPickable) {
+ viewHolder.mBtInfo = (ImageButton)v.findViewById(RESOURCE_ITEM_INFO);
+ viewHolder.mBtInfo.setOnClickListener(this);
+ } else {
+ viewHolder.mBtInfo = (ImageButton)v.findViewById(RESOURCE_ITEM_INFO);
+ viewHolder.mBtInfo.setVisibility(View.GONE);
+ }
+ v.setTag(viewHolder);
+ }
+
+ //Retrieve data holder
+ final DataHolder dataHolder = this.mData[position];
+
+ //Retrieve the view holder
+ ViewHolder viewHolder = (ViewHolder)v.getTag();
+
+ //Set the data
+ //Gather image thumbnail or generate apk icon if it hasn't been generated yet
+ if (displayThumbs && this.mData[position].mImagePath != null && !this.mData[position].mImagePath.isEmpty()) {
+ viewHolder.mBtIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ RelativeLayout.LayoutParams lp = ((RelativeLayout.LayoutParams)viewHolder.mBtIcon.getLayoutParams());
+ if(lp != null)
+ {
+ lp.setMargins(0, 0, 0, 0);
+ viewHolder.mBtIcon.setLayoutParams(lp);
+ }
+ mImageFetcher.loadImage(this.mData[position].mImagePath, viewHolder.mBtIcon);
+ } else {
+ viewHolder.mBtIcon.setImageDrawable(dataHolder.mDwIcon);
+ }
+ viewHolder.mTvName.setText(dataHolder.mName);
+ if (viewHolder.mTvSummary != null) {
+ viewHolder.mTvSummary.setText(dataHolder.mSummary);
+ }
+ if (viewHolder.mTvSize != null) {
+ viewHolder.mTvSize.setText(dataHolder.mSize);
+ }
+ if (!this.mPickable) {
+ viewHolder.mBtInfo.setVisibility(
+ dataHolder.mName.compareTo(
+ FileHelper.PARENT_DIRECTORY) == 0 ? View.INVISIBLE : View.VISIBLE);
+ viewHolder.mBtIcon.setOnClickListener(this);
+ viewHolder.mBtIcon.setTag(Integer.valueOf(position));
+ viewHolder.mBtInfo.setTag(Integer.valueOf(position));
+
+ if (viewHolder.mHasSelectedBg == null
+ || viewHolder.mHasSelectedBg != dataHolder.mSelected) {
+ String drawableId = dataHolder.mSelected
+ ? "selectors_selected_drawable" //$NON-NLS-1$
+ : "selectors_deselected_drawable"; //$NON-NLS-1$
+
+ theme.setBackgroundDrawable(getContext(), v, drawableId);
+ viewHolder.mHasSelectedBg = dataHolder.mSelected;
+ }
+ }
+
+ //Return the view
+ return v;
+ }
+
+ /**
+ * Method that returns if the item of the passed position is selected.
+ *
+ * @param position The position of the item
+ * @return boolean If the item of the passed position is selected
+ */
+ public boolean isSelected(int position) {
+ return this.mData[position].mSelected;
+ }
+
+ /**
+ * Method that selects in the {@link ArrayAdapter} the passed item.
+ *
+ * @param fso The file system object to select
+ */
+ public void toggleSelection(FileSystemObject fso) {
+ toggleSelection(null, fso);
+ }
+
+ /**
+ * Method that selects in the {@link ArrayAdapter} the passed item.
+ *
+ * @param v The check view object (can be null)
+ * @param fso The file system object to select
+ */
+ private void toggleSelection(View v, FileSystemObject fso) {
+ if (this.mData != null) {
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+ int cc = this.mData.length;
+ for (int i = 0; i < cc; i++) {
+ DataHolder data = this.mData[i];
+ if (data.mName.compareTo(fso.getName()) == 0) {
+ //Select/Deselect the item
+ data.mSelected = !data.mSelected;
+ if (v != null) {
+ View viewParent = (View)v.getParent().getParent();
+
+ viewParent.setSelected(data.mSelected);
+
+ if (data.mSelected) {
+ theme.setBackgroundDrawable(
+ getContext(),
+ viewParent,
+ "selectors_selected_drawable"); //$NON-NLS-1$
+ } else {
+ theme.setBackgroundDrawable(
+ getContext(),
+ viewParent,
+ "selectors_deselected_drawable"); //$NON-NLS-1$
+ }
+ }
+
+ //Add or remove from the global selected items
+ final List selectedItems =
+ FileSystemObjectAdapter.this.mSelectedItems;
+ if (data.mSelected) {
+ if (!selectedItems.contains(fso)) {
+ selectedItems.add(fso);
+ }
+ } else {
+ if (selectedItems.contains(fso)) {
+ selectedItems.remove(fso);
+ }
+ }
+
+ //Communicate event
+ if (this.mOnSelectionChangedListener != null) {
+ List selection =
+ new ArrayList(selectedItems);
+ this.mOnSelectionChangedListener.onSelectionChanged(selection);
+ }
+
+ // The internal structure was update, only super adapter need to be notified
+ super.notifyDataSetChanged();
+
+ //Found
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Method that deselect all items.
+ */
+ public void deselectedAll() {
+ this.mSelectedItems.clear();
+ doSelectDeselectAllVisibleItems(false);
+ }
+
+ /**
+ * Method that select all visible items.
+ */
+ public void selectedAllVisibleItems() {
+ doSelectDeselectAllVisibleItems(true);
+ }
+
+ /**
+ * Method that deselect all visible items.
+ */
+ public void deselectedAllVisibleItems() {
+ doSelectDeselectAllVisibleItems(false);
+ }
+
+ /**
+ * Method that select/deselect all items.
+ *
+ * @param select Indicates if select (true) or deselect (false) all items.
+ */
+ private void doSelectDeselectAllVisibleItems(boolean select) {
+ if (this.mData != null && this.mData.length > 0) {
+ // Clear mSelectedItems. Both deselect all and select all require a blank slate.
+ FileSystemObjectAdapter.this.mSelectedItems.clear();
+
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+ int cc = this.mData.length;
+ for (int i = 0; i < cc; i++) {
+ DataHolder data = this.mData[i];
+ if (data.mName.compareTo(FileHelper.PARENT_DIRECTORY) == 0) {
+ // No select the parent directory
+ continue;
+ }
+ data.mSelected = select;
+ if (data.mSelected) {
+ data.mDwCheck =
+ theme.getDrawable(
+ getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
+ } else {
+ data.mDwCheck =
+ theme.getDrawable(
+ getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
+ }
+
+ //Add or remove from the global selected items
+ FileSystemObject fso = getItem(i);
+ final List selectedItems =
+ FileSystemObjectAdapter.this.mSelectedItems;
+ if (data.mSelected) {
+ if (!selectedItems.contains(fso)) {
+ selectedItems.add(fso);
+ }
+ } else {
+ if (selectedItems.contains(fso)) {
+ selectedItems.remove(fso);
+ }
+ }
+ }
+
+ //Communicate event
+ if (this.mOnSelectionChangedListener != null) {
+ List selection =
+ new ArrayList(
+ FileSystemObjectAdapter.this.mSelectedItems);
+ this.mOnSelectionChangedListener.onSelectionChanged(selection);
+ }
+
+ // The internal structure was update, only super adapter need to be notified
+ super.notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Method that returns the selected items.
+ *
+ * @return List The selected items
+ */
+ public List getSelectedItems() {
+ return new ArrayList(this.mSelectedItems);
+ }
+
+ /**
+ * Method that sets the selected items.
+ *
+ * @param selectedItems The selected items
+ */
+ public void setSelectedItems(List selectedItems) {
+ this.mSelectedItems = selectedItems;
+ }
+
+ public int getSelectedItemsCount() {
+ return mSelectedItems.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onClick(View v) {
+
+ //Select or deselect the item
+ int pos = ((Integer)v.getTag()).intValue();
+
+ //Retrieve data holder
+ final FileSystemObject fso = getItem(pos);
+
+ //What button was pressed?
+ switch (v.getId()) {
+ case RESOURCE_ITEM_ICON:
+ // If we're not dealing with a parent folder item, toggle it
+ if (fso.getName().compareTo(FileHelper.PARENT_DIRECTORY) != 0){
+ toggleSelection(v, fso);
+ }
+ break;
+ case RESOURCE_ITEM_INFO:
+ EventBus.getDefault().post(new OpenPropertiesDrawerEvent(fso));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Method that should be invoked when the theme of the app was changed
+ */
+ public void notifyThemeChanged() {
+ // Empty icon holder
+ if (this.mIconHolder != null) {
+ this.mIconHolder.cleanup();
+ }
+ displayThumbs = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getDefaultValue()).booleanValue());
+ this.mIconHolder = new IconHolder(getContext());
+ loadDefaultIcons();
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/HistoryAdapter.java
similarity index 87%
rename from src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java
rename to Backbone/src/main/java/me/toolify/backbone/adapters/HistoryAdapter.java
index 4582410f6..4b1fcda91 100644
--- a/src/com/cyanogenmod/filemanager/adapters/HistoryAdapter.java
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/HistoryAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.adapters;
+package me.toolify.backbone.adapters;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -25,13 +25,13 @@
import android.widget.ImageView;
import android.widget.TextView;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.model.History;
-import com.cyanogenmod.filemanager.parcelables.NavigationViewInfoParcelable;
-import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
-import com.cyanogenmod.filemanager.ui.IconHolder;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import me.toolify.backbone.R;
+import me.toolify.backbone.model.History;
+import me.toolify.backbone.parcelables.NavigationViewInfoParcelable;
+import me.toolify.backbone.parcelables.SearchInfoParcelable;
+import me.toolify.backbone.ui.IconHolder;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
import java.util.List;
@@ -98,7 +98,7 @@ public DataHolder() {
*/
public HistoryAdapter(Context context, List history) {
super(context, RESOURCE_ITEM_NAME, history);
- this.mIconHolder = new IconHolder();
+ notifyThemeChanged(); // Reload icons
//Do cache of the data for better performance
processData(history);
@@ -119,7 +119,10 @@ public void notifyDataSetChanged() {
public void dispose() {
clear();
this.mData = null;
- this.mIconHolder = null;
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ mIconHolder = null;
+ }
}
/**
@@ -138,12 +141,10 @@ private void processData(List historyData) {
this.mData[i] = new HistoryAdapter.DataHolder();
if (history.getItem() instanceof NavigationViewInfoParcelable) {
this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
} else if (history.getItem() instanceof SearchInfoParcelable) {
this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), "ic_history_search_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_history_search_drawable"); //$NON-NLS-1$
}
this.mData[i].mName = history.getItem().getTitle();
if (this.mData[i].mName == null || this.mData[i].mName.trim().length() == 0) {
@@ -207,8 +208,11 @@ public View getView(int position, View convertView, ViewGroup parent) {
* Method that should be invoked when the theme of the app was changed
*/
public void notifyThemeChanged() {
- // Empty icon holder
- this.mIconHolder = new IconHolder();
+ if (mIconHolder != null) {
+ mIconHolder.cleanup();
+ }
+ // Empty icon holder (only have folders and search icons)
+ this.mIconHolder = new IconHolder(getContext());
}
}
diff --git a/Backbone/src/main/java/me/toolify/backbone/adapters/LicenseAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/LicenseAdapter.java
new file mode 100644
index 000000000..ad1263050
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/LicenseAdapter.java
@@ -0,0 +1,178 @@
+/*
+ * 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 me.toolify.backbone.adapters;
+
+import android.content.Context;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import me.toolify.backbone.R;
+import me.toolify.backbone.model.License;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+
+import java.util.List;
+
+/**
+ * An implementation of {@link android.widget.ArrayAdapter} for display history.
+ */
+public class LicenseAdapter extends ArrayAdapter {
+
+ /**
+ * A class that conforms with the ViewHolder pattern to performance
+ * the list view rendering.
+ */
+ private static class ViewHolder {
+ /**
+ * @hide
+ */
+ public ViewHolder() {
+ super();
+ }
+ TextView mTvHeader;
+ TextView mTvDetail;
+ }
+
+ /**
+ * A class that holds the full data information.
+ */
+ private static class DataHolder {
+ /**
+ * @hide
+ */
+ public DataHolder() {
+ super();
+ }
+ String mHeader;
+ String mDetail;
+ }
+
+ private DataHolder[] mData;
+
+ //The resource item layout
+ private static final int RESOURCE_LAYOUT = R.layout.license_item;
+
+ //The resource of the item name
+ private static final int RESOURCE_ITEM_HEADER = R.id.license_header;
+ //The resource of the item directory
+ private static final int RESOURCE_ITEM_DETAIL = R.id.license_details;
+
+ /**
+ * Constructor of HistoryAdapter.
+ *
+ * @param context The current context
+ * @param licenses The license list reference
+ */
+ public LicenseAdapter(Context context, List licenses) {
+ super(context, RESOURCE_ITEM_HEADER, licenses);
+
+ //Do cache of the data for better performance
+ processData(licenses);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyDataSetChanged() {
+ processData(null);
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Method that dispose the elements of the adapter.
+ */
+ public void dispose() {
+ clear();
+ this.mData = null;
+ }
+
+ /**
+ * Method that process the data before use {@link #getView} method.
+ *
+ * @param licenseData The list of licenses (to better performance) or null.
+ */
+ private void processData(List licenseData) {
+ this.mData = new DataHolder[getCount()];
+ int cc = (licenseData == null) ? getCount() : licenseData.size();
+ for (int i = 0; i < cc; i++) {
+ //History info
+ License license = (licenseData == null) ? getItem(i) : licenseData.get(i);
+
+ //Build the data holder
+ this.mData[i] = new LicenseAdapter.DataHolder();
+ this.mData[i].mHeader = license.mHeader;
+ this.mData[i].mDetail = license.mDetail;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ //Check to reuse view
+ View v = convertView;
+ if (v == null) {
+ //Create the view holder
+ LayoutInflater li =
+ (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ v = li.inflate(RESOURCE_LAYOUT, parent, false);
+ ViewHolder viewHolder = new LicenseAdapter.ViewHolder();
+ viewHolder.mTvHeader = (TextView)v.findViewById(RESOURCE_ITEM_HEADER);
+ viewHolder.mTvDetail = (TextView)v.findViewById(RESOURCE_ITEM_DETAIL);
+ v.setTag(viewHolder);
+
+ // Apply the current theme
+ Theme theme = ThemeManager.getCurrentTheme(getContext());
+ theme.setBackgroundDrawable(
+ getContext(), v, "selectors_deselected_drawable"); //$NON-NLS-1$
+ theme.setTextColor(
+ getContext(), viewHolder.mTvHeader, "text_color"); //$NON-NLS-1$
+ theme.setTextColor(
+ getContext(), viewHolder.mTvDetail, "text_color"); //$NON-NLS-1$
+ }
+
+ //Retrieve data holder
+ final DataHolder dataHolder = this.mData[position];
+
+ //Retrieve the view holder
+ ViewHolder viewHolder = (ViewHolder)v.getTag();
+
+ //Set the data
+ viewHolder.mTvHeader.setText(Html.fromHtml(dataHolder.mHeader));
+ viewHolder.mTvHeader.setMovementMethod(LinkMovementMethod.getInstance());
+ viewHolder.mTvDetail.setText(Html.fromHtml(dataHolder.mDetail));
+ viewHolder.mTvDetail.setMovementMethod(LinkMovementMethod.getInstance());
+
+ //Return the view
+ return v;
+ }
+
+ /**
+ * Method that should be invoked when the theme of the app was changed
+ */
+ public void notifyThemeChanged() {
+
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/adapters/MenuSettingsAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/MenuSettingsAdapter.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/adapters/MenuSettingsAdapter.java
rename to Backbone/src/main/java/me/toolify/backbone/adapters/MenuSettingsAdapter.java
index b9bc1b6ae..6fdd496c6 100644
--- a/src/com/cyanogenmod/filemanager/adapters/MenuSettingsAdapter.java
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/MenuSettingsAdapter.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.adapters;
+package me.toolify.backbone.adapters;
import android.content.Context;
import android.content.res.Resources;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
-import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.util.ResourcesHelper;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.ObjectIdentifier;
+import me.toolify.backbone.preferences.ObjectStringIdentifier;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.util.ResourcesHelper;
import java.lang.reflect.Method;
import java.util.ArrayList;
diff --git a/Backbone/src/main/java/me/toolify/backbone/adapters/NavigationFragmentPagerAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/NavigationFragmentPagerAdapter.java
new file mode 100644
index 000000000..a13274fad
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/NavigationFragmentPagerAdapter.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.adapters;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.annotation.TargetApi;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.support.v4.view.PagerAdapter;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.fragments.NavigationFragment;
+
+/**
+ * Implementation of {@link android.support.v4.view.PagerAdapter} that
+ * represents each page as a {@link NavigationFragment} that is persistently kept in the
+ * fragment manager as long as the user can return to the page.
+ *
+ *
+ */
+public class NavigationFragmentPagerAdapter extends PagerAdapter {
+ private static final String TAG = "FragmentPagerAdapter";
+ private static final boolean DEBUG = false;
+
+ private final FragmentManager mFragmentManager;
+ private FragmentTransaction mCurTransaction = null;
+ private Fragment mCurrentPrimaryItem = null;
+
+ private int mNumPages;
+
+ public NavigationFragmentPagerAdapter(Context context, FragmentManager fm) {
+ mFragmentManager = fm;
+ this.mNumPages = FileManagerApplication.NUM_PAGES;
+ }
+
+ /**
+ * Return the Fragment associated with a specified position.
+ *
+ * @param position the integer position of the requested fragment within the Pager Adapter
+ */
+ public Fragment getItem(int position) {
+ NavigationFragment myFragment = NavigationFragment.newInstance(position);
+ return myFragment;
+ }
+
+ /**
+ * This function is the reason why this class is a full copy of
+ * FragmentPagerAdapter and not an implementation. The app required the
+ * ability to call functions on each FileListFragment from the main class.
+ * This function needed to be able to reference mFragmentManger.
+ *
+ * @param container
+ * @param position
+ * @return FileListFragment
+ */
+ public NavigationFragment getFragment(ViewGroup container, int position) {
+ String name = makeFragmentName(container.getId(), position);
+ NavigationFragment fragment = (NavigationFragment) mFragmentManager.findFragmentByTag(name);
+ return fragment;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int getCount() {
+ return mNumPages;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void startUpdate(ViewGroup container) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @TargetApi(15)
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ // Do we already have this fragment?
+ String name = makeFragmentName(container.getId(), position);
+ Fragment fragment = mFragmentManager.findFragmentByTag(name);
+ if (fragment != null) {
+ if (DEBUG)
+ Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
+ mCurTransaction.attach(fragment);
+ } else {
+ fragment = getItem(position);
+ if (DEBUG)
+ Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
+ mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), position));
+ }
+ if (fragment != mCurrentPrimaryItem) {
+ fragment.setMenuVisibility(false);
+ if (android.os.Build.VERSION.SDK_INT >= 15) {
+ fragment.setUserVisibleHint(false);
+ }
+ }
+
+ return fragment;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ if (DEBUG)
+ Log.v(TAG, "Detaching item #" + position + ": f=" + object + " v=" + ((Fragment) object).getView());
+ mCurTransaction.detach((Fragment) object);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @TargetApi(15)
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment) object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ if (android.os.Build.VERSION.SDK_INT >= 15) {
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ }
+ if (fragment != null) {
+ fragment.setMenuVisibility(true);
+ if (android.os.Build.VERSION.SDK_INT >= 15) {
+ fragment.setUserVisibleHint(true);
+ }
+ }
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void finishUpdate(ViewGroup container) {
+ if (mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ mFragmentManager.executePendingTransactions();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((Fragment) object).getView() == view;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Parcelable saveState() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void restoreState(Parcelable state, ClassLoader loader) {
+ }
+
+ private static String makeFragmentName(int viewId, int index) {
+ return "android:switcher:" + viewId + ":" + index;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return "Fragment" + position;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java b/Backbone/src/main/java/me/toolify/backbone/adapters/SearchResultAdapter.java
similarity index 83%
rename from src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java
rename to Backbone/src/main/java/me/toolify/backbone/adapters/SearchResultAdapter.java
index 0561a6473..2441832f9 100644
--- a/src/com/cyanogenmod/filemanager/adapters/SearchResultAdapter.java
+++ b/Backbone/src/main/java/me/toolify/backbone/adapters/SearchResultAdapter.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2013 BrandroidTools
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +15,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.adapters;
+package me.toolify.backbone.adapters;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -25,18 +26,18 @@
import android.widget.ImageView;
import android.widget.TextView;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.model.FileSystemObject;
-import com.cyanogenmod.filemanager.model.Query;
-import com.cyanogenmod.filemanager.model.SearchResult;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.ui.IconHolder;
-import com.cyanogenmod.filemanager.ui.ThemeManager;
-import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
-import com.cyanogenmod.filemanager.ui.widgets.RelevanceView;
-import com.cyanogenmod.filemanager.util.MimeTypeHelper;
-import com.cyanogenmod.filemanager.util.SearchHelper;
+import me.toolify.backbone.R;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.model.SearchResult;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.ui.IconHolder;
+import me.toolify.backbone.ui.ThemeManager;
+import me.toolify.backbone.ui.ThemeManager.Theme;
+import me.toolify.backbone.ui.widgets.RelevanceView;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.SearchHelper;
import java.io.File;
import java.util.ArrayList;
@@ -80,6 +81,7 @@ public DataHolder() {
Float mRelevance;
}
+ private static final int MESSAGE_REDRAW = 1;
private DataHolder[] mData;
private IconHolder mIconHolder;
@@ -90,6 +92,8 @@ public DataHolder() {
private final List mQueries;
+ private boolean mDisposed;
+
//The resource of the item icon
private static final int RESOURCE_ITEM_ICON = R.id.search_item_icon;
//The resource of the item name
@@ -111,7 +115,11 @@ public DataHolder() {
public SearchResultAdapter(
Context context, List files, int itemViewResourceId, Query queries) {
super(context, RESOURCE_ITEM_NAME, files);
- this.mIconHolder = new IconHolder();
+ this.mDisposed = false;
+ final boolean displayThumbs = Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
+ ((Boolean)FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getDefaultValue()).booleanValue());
+ this.mIconHolder = new IconHolder(context);
this.mItemViewResourceId = itemViewResourceId;
this.mQueries = queries.getQueries();
@@ -127,15 +135,15 @@ public SearchResultAdapter(
//Do cache of the data for better performance
loadDefaultIcons();
- processData(files);
+ processData();
}
/**
* Method that loads the default icons (known icons and more common icons).
*/
private void loadDefaultIcons() {
- this.mIconHolder.getDrawable(getContext(), "ic_fso_folder_drawable"); //$NON-NLS-1$
- this.mIconHolder.getDrawable(getContext(), "ic_fso_default_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
+ this.mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$
}
/**
@@ -143,7 +151,10 @@ private void loadDefaultIcons() {
*/
@Override
public void notifyDataSetChanged() {
- processData(null);
+ if (this.mDisposed) {
+ return;
+ }
+ processData();
super.notifyDataSetChanged();
}
@@ -151,6 +162,10 @@ public void notifyDataSetChanged() {
* Method that dispose the elements of the adapter.
*/
public void dispose() {
+ if (this.mIconHolder != null) {
+ this.mIconHolder.cleanup();
+ }
+ this.mDisposed = true;
clear();
this.mData = null;
this.mIconHolder = null;
@@ -158,25 +173,23 @@ public void dispose() {
/**
* Method that process the data before use {@link #getView} method.
- *
- * @param files The list of files (to better performance) or null.
*/
- private void processData(List files) {
+ private void processData() {
Theme theme = ThemeManager.getCurrentTheme(getContext());
int highlightedColor =
theme.getColor(getContext(), "search_highlight_color"); //$NON-NLS-1$
this.mData = new DataHolder[getCount()];
- int cc = (files == null) ? getCount() : files.size();
+ int cc = getCount();
for (int i = 0; i < cc; i++) {
//File system object info
- SearchResult result = (files == null) ? getItem(i) : files.get(i);
+ SearchResult result = getItem(i);
//Build the data holder
+ final FileSystemObject fso = result.getFso();
this.mData[i] = new SearchResultAdapter.DataHolder();
- this.mData[i].mDwIcon =
- this.mIconHolder.getDrawable(
- getContext(), MimeTypeHelper.getIcon(getContext(), result.getFso()));
+ this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
+ MimeTypeHelper.getIcon(getContext(), fso));
if (this.mHighlightTerms) {
this.mData[i].mName =
SearchHelper.getHighlightedName(result, this.mQueries, highlightedColor);
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkDeleteEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkDeleteEvent.java
new file mode 100644
index 000000000..4e040eed7
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkDeleteEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+public class BookmarkDeleteEvent extends BusEvent {
+ public final String path;
+
+ public BookmarkDeleteEvent(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("(")
+ .append("bus event: delete bookmark ")
+ .append(path)
+ .append(")")
+ .toString();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkOpenEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkOpenEvent.java
new file mode 100644
index 000000000..76697e89a
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkOpenEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+public class BookmarkOpenEvent extends BusEvent {
+ public final String path;
+
+ public BookmarkOpenEvent(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("(")
+ .append("bus event: open bookmark ")
+ .append(path)
+ .append(")")
+ .toString();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkRefreshEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkRefreshEvent.java
new file mode 100644
index 000000000..586ad1706
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/BookmarkRefreshEvent.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+public class BookmarkRefreshEvent extends BusEvent {
+
+ public void BookmarkRefreshEvent() {
+
+ }
+}
\ No newline at end of file
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/BusEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/BusEvent.java
new file mode 100644
index 000000000..d6ab61b49
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/BusEvent.java
@@ -0,0 +1,13 @@
+package me.toolify.backbone.bus.events;
+
+/**
+ * Created by Brandon on 9/25/13.
+ */
+public abstract class BusEvent {
+ @Override
+ public String toString() {
+ return new StringBuilder("(bus event: ")
+ .append(this.getClass().toString())
+ .append(")").toString();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/ClosePropertiesDrawerEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/ClosePropertiesDrawerEvent.java
new file mode 100644
index 000000000..9679f9af6
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/ClosePropertiesDrawerEvent.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+public class ClosePropertiesDrawerEvent extends BusEvent {
+
+ public ClosePropertiesDrawerEvent() {
+
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/FilesystemStatusUpdateEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/FilesystemStatusUpdateEvent.java
new file mode 100644
index 000000000..2dad0faa3
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/FilesystemStatusUpdateEvent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+public class FilesystemStatusUpdateEvent extends BusEvent {
+ public final int status;
+
+ public static final int INDICATOR_LOCKED = 0;
+ public static final int INDICATOR_UNLOCKED = 1;
+ public static final int INDICATOR_WARNING = 2;
+ public static final int INDICATOR_REFRESHING = 3;
+ public static final int INDICATOR_STOP_REFRESHING = 4;
+
+ public FilesystemStatusUpdateEvent(int status) {
+ this.status = status;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("(")
+ .append("bus event: filesystem info status update")
+ .append(" - status code: ")
+ .append(Integer.toString(status))
+ .append(")")
+ .toString();
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/bus/events/OpenPropertiesDrawerEvent.java b/Backbone/src/main/java/me/toolify/backbone/bus/events/OpenPropertiesDrawerEvent.java
new file mode 100644
index 000000000..599380f24
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/bus/events/OpenPropertiesDrawerEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) BrandroidTools
+ *
+ * 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 me.toolify.backbone.bus.events;
+
+import me.toolify.backbone.model.FileSystemObject;
+
+public class OpenPropertiesDrawerEvent extends BusEvent {
+ public final Object item;
+
+ public OpenPropertiesDrawerEvent(Object item) {
+ this.item = item;
+ }
+
+ @Override
+ public String toString() {
+ String path = String.valueOf(item);
+ if (item instanceof FileSystemObject) {
+ path = ((FileSystemObject)item).getFullPath();
+ }
+ return new StringBuilder("(")
+ .append("bus event: open properties on file \"")
+ .append(path)
+ .append("\")")
+ .toString();
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/AsyncResultExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultExecutable.java
similarity index 98%
rename from src/com/cyanogenmod/filemanager/commands/AsyncResultExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultExecutable.java
index c53aa1920..9a3684d34 100644
--- a/src/com/cyanogenmod/filemanager/commands/AsyncResultExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that defines a class as executable in an asynchronous way.
diff --git a/src/com/cyanogenmod/filemanager/commands/AsyncResultListener.java b/Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultListener.java
similarity index 97%
rename from src/com/cyanogenmod/filemanager/commands/AsyncResultListener.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultListener.java
index 847bd1a1d..781e4c814 100644
--- a/src/com/cyanogenmod/filemanager/commands/AsyncResultListener.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/AsyncResultListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
diff --git a/src/com/cyanogenmod/filemanager/commands/ChangeOwnerExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ChangeOwnerExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/ChangeOwnerExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ChangeOwnerExecutable.java
index 9d0b9c359..05294e6a3 100644
--- a/src/com/cyanogenmod/filemanager/commands/ChangeOwnerExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ChangeOwnerExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for change the owner of
diff --git a/src/com/cyanogenmod/filemanager/commands/ChangePermissionsExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ChangePermissionsExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/ChangePermissionsExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ChangePermissionsExecutable.java
index 0cb2c8447..8146f25cf 100644
--- a/src/com/cyanogenmod/filemanager/commands/ChangePermissionsExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ChangePermissionsExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for change the permissions of
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/ChecksumExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ChecksumExecutable.java
new file mode 100644
index 000000000..afe6b8c10
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ChecksumExecutable.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.toolify.backbone.commands;
+
+/**
+ * An interface that represents an executable for calculate checksum of file system objects.
+ */
+public interface ChecksumExecutable extends AsyncResultExecutable {
+
+ /**
+ * Checksum enumerations
+ */
+ public enum CHECKSUMS {
+ /**
+ * MD5 digest algorithm
+ */
+ MD5,
+ /**
+ * SHA-1 digest algorithm
+ */
+ SHA1
+ }
+
+ /**
+ * Method that returns the calculated MD5 [0] and SHA-1 [1] digests
+ *
+ * @return String[] The calculated MD5 [0] and SHA-1 [1] digests
+ */
+ String[] getResult();
+
+ /**
+ * Method that returns a calculated digest checksum
+ *
+ * @param checksum The checksum to return
+ * @return String The calculated digest to return
+ */
+ String getChecksum(CHECKSUMS checksum);
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/CompressExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/CompressExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/CompressExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/CompressExecutable.java
index af8ce5433..8b6ce49fd 100644
--- a/src/com/cyanogenmod/filemanager/commands/CompressExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/CompressExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for compress file system objects.
diff --git a/src/com/cyanogenmod/filemanager/commands/CopyExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/CopyExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/CopyExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/CopyExecutable.java
index 983d84446..e8f9a78df 100644
--- a/src/com/cyanogenmod/filemanager/commands/CopyExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/CopyExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for copy a file system object to
diff --git a/src/com/cyanogenmod/filemanager/commands/CreateDirExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/CreateDirExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/CreateDirExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/CreateDirExecutable.java
index 40d490e06..a05dcf448 100644
--- a/src/com/cyanogenmod/filemanager/commands/CreateDirExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/CreateDirExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for create a new directory.
diff --git a/src/com/cyanogenmod/filemanager/commands/CreateFileExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/CreateFileExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/CreateFileExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/CreateFileExecutable.java
index 011aaf518..365d23cfe 100644
--- a/src/com/cyanogenmod/filemanager/commands/CreateFileExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/CreateFileExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for create a new file.
diff --git a/src/com/cyanogenmod/filemanager/commands/DeleteDirExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/DeleteDirExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/DeleteDirExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/DeleteDirExecutable.java
index 0f2ce04b8..e909fc481 100644
--- a/src/com/cyanogenmod/filemanager/commands/DeleteDirExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/DeleteDirExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for delete a new directory.
diff --git a/src/com/cyanogenmod/filemanager/commands/DeleteFileExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/DeleteFileExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/DeleteFileExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/DeleteFileExecutable.java
index 33a9f235b..a60e47f43 100644
--- a/src/com/cyanogenmod/filemanager/commands/DeleteFileExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/DeleteFileExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for delete a new file.
diff --git a/src/com/cyanogenmod/filemanager/commands/DiskUsageExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/DiskUsageExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/DiskUsageExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/DiskUsageExecutable.java
index 31beb761c..b35933fd9 100644
--- a/src/com/cyanogenmod/filemanager/commands/DiskUsageExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/DiskUsageExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.DiskUsage;
+import me.toolify.backbone.model.DiskUsage;
import java.util.List;
diff --git a/src/com/cyanogenmod/filemanager/commands/EchoExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/EchoExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/EchoExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/EchoExecutable.java
index a1ff41b11..5d32b954b 100644
--- a/src/com/cyanogenmod/filemanager/commands/EchoExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/EchoExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for expanding environment variables
diff --git a/src/com/cyanogenmod/filemanager/commands/ExecExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ExecExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/ExecExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ExecExecutable.java
index 5cf676282..3436b09c4 100644
--- a/src/com/cyanogenmod/filemanager/commands/ExecExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ExecExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for execute a command
diff --git a/src/com/cyanogenmod/filemanager/commands/Executable.java b/Backbone/src/main/java/me/toolify/backbone/commands/Executable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/Executable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/Executable.java
index 874235d98..c495d41fb 100644
--- a/src/com/cyanogenmod/filemanager/commands/Executable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/Executable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that defines a class as executable.
diff --git a/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java b/Backbone/src/main/java/me/toolify/backbone/commands/ExecutableCreator.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ExecutableCreator.java
index a0fd6f94b..3c414431e 100644
--- a/src/com/cyanogenmod/filemanager/commands/ExecutableCreator.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ExecutableCreator.java
@@ -14,38 +14,24 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
-
-import com.cyanogenmod.filemanager.commands.ListExecutable.LIST_MODE;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.model.Permissions;
-import com.cyanogenmod.filemanager.model.Query;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.preferences.CompressionMode;
+package me.toolify.backbone.commands;
+
+import me.toolify.backbone.commands.ListExecutable.LIST_MODE;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.model.Permissions;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.model.User;
+import me.toolify.backbone.preferences.CompressionMode;
/**
* A interface that defines methods for create {@link Executable} objects.
*/
public interface ExecutableCreator {
- /**
- * Method that creates an executable for change the current directory.
- *
- * @param dir The absolute path of the new directory to establish as current directory
- * @return ChangeCurrentDirExecutable A {@link ChangeCurrentDirExecutable} executable
- * implementation reference
- * @throws CommandNotFoundException If the executable can't be created
- * @throws NoSuchFileOrDirectory If the file or directory was not found
- * @throws InsufficientPermissionsException If an operation requires elevated permissions
- */
- ChangeCurrentDirExecutable createChangeCurrentDirExecutable(
- String dir) throws CommandNotFoundException,
- NoSuchFileOrDirectory, InsufficientPermissionsException;
-
/**
* Method that creates an executable for change the owner of a file system object.
*
@@ -118,18 +104,6 @@ CreateDirExecutable createCreateDirectoryExecutable(String dir)
CreateFileExecutable createCreateFileExecutable(String file) throws CommandNotFoundException,
NoSuchFileOrDirectory, InsufficientPermissionsException;
- /**
- * Method that creates an executable for retrieve the current directory.
- *
- * @return CurrentDirExecutable A {@link CurrentDirExecutable} executable
- * implementation reference
- * @throws CommandNotFoundException If the executable can't be created
- * @throws NoSuchFileOrDirectory If the file or directory was not found
- * @throws InsufficientPermissionsException If an operation requires elevated permissions
- */
- CurrentDirExecutable createCurrentDirExecutable() throws CommandNotFoundException,
- NoSuchFileOrDirectory, InsufficientPermissionsException;
-
/**
* Method that creates an executable for delete a directory.
*
@@ -251,7 +225,7 @@ FolderUsageExecutable createFolderUsageExecutable(
* @throws InsufficientPermissionsException If an operation requires elevated permissions
*/
GroupsExecutable createGroupsExecutable()
- throws com.cyanogenmod.filemanager.console.CommandNotFoundException,
+ throws me.toolify.backbone.console.CommandNotFoundException,
NoSuchFileOrDirectory, InsufficientPermissionsException;
/**
@@ -375,6 +349,21 @@ ParentDirExecutable createParentDirExecutable(String fso) throws CommandNotFound
ProcessIdExecutable createShellProcessIdExecutable() throws CommandNotFoundException,
NoSuchFileOrDirectory, InsufficientPermissionsException;
+ /**
+ * Method that creates an executable for retrieve operating system process identifiers of a
+ * shell.
+ *
+ * @param pid The shell process id where the process is running
+ * @param processName The process name
+ * @return ProcessIdExecutable A {@link ProcessIdExecutable} executable implementation
+ * reference
+ * @throws CommandNotFoundException If the executable can't be created
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ */
+ ProcessIdExecutable createProcessIdExecutable(int pid) throws CommandNotFoundException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException;
+
/**
* Method that creates an executable for retrieve operating system process identifier of a
* process.
@@ -529,4 +518,19 @@ UncompressExecutable createUncompressExecutable(
throws CommandNotFoundException,
NoSuchFileOrDirectory, InsufficientPermissionsException;
+ /**
+ * Method that creates an executable for calculate checksums of file system objects.
+ *
+ * @param src The compressed file
+ * @param asyncResultListener The listener where to return partial results
+ * @return ChecksumExecutable A {@link ChecksumExecutable} executable implementation reference
+ * @throws CommandNotFoundException If the executable can't be created
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ */
+ ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException,
+ NoSuchFileOrDirectory, InsufficientPermissionsException;
+
}
diff --git a/src/com/cyanogenmod/filemanager/commands/ExecutableFactory.java b/Backbone/src/main/java/me/toolify/backbone/commands/ExecutableFactory.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/ExecutableFactory.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ExecutableFactory.java
index 32def5cb1..97bf8e5fb 100644
--- a/src/com/cyanogenmod/filemanager/commands/ExecutableFactory.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ExecutableFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* A class that represents a factory for creating {@link Executable} objects.
diff --git a/src/com/cyanogenmod/filemanager/commands/FindExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/FindExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/FindExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/FindExecutable.java
index 7bcee56fa..ed7d61ba7 100644
--- a/src/com/cyanogenmod/filemanager/commands/FindExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/FindExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for make a search over
diff --git a/src/com/cyanogenmod/filemanager/commands/FolderUsageExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/FolderUsageExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/FolderUsageExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/FolderUsageExecutable.java
index d9e1253a6..cf8b0e0b3 100644
--- a/src/com/cyanogenmod/filemanager/commands/FolderUsageExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/FolderUsageExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.FolderUsage;
+import me.toolify.backbone.model.FolderUsage;
/**
* An interface that represents an executable for retrieve a folder usage
diff --git a/src/com/cyanogenmod/filemanager/commands/GroupsExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/GroupsExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/GroupsExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/GroupsExecutable.java
index 30615d7c5..59d4cb280 100644
--- a/src/com/cyanogenmod/filemanager/commands/GroupsExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/GroupsExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.Group;
+import me.toolify.backbone.model.Group;
import java.util.List;
diff --git a/src/com/cyanogenmod/filemanager/commands/IdentityExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/IdentityExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/IdentityExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/IdentityExecutable.java
index 8508ef7b9..e14397e72 100644
--- a/src/com/cyanogenmod/filemanager/commands/IdentityExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/IdentityExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.Identity;
+import me.toolify.backbone.model.Identity;
/**
* An interface that represents an executable for retrieve information of
diff --git a/src/com/cyanogenmod/filemanager/commands/LinkExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/LinkExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/LinkExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/LinkExecutable.java
index 53833ee91..405c517cd 100644
--- a/src/com/cyanogenmod/filemanager/commands/LinkExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/LinkExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for create symlinks to other file system objects.
diff --git a/src/com/cyanogenmod/filemanager/commands/ListExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ListExecutable.java
similarity index 92%
rename from src/com/cyanogenmod/filemanager/commands/ListExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ListExecutable.java
index 071177904..9138d975e 100644
--- a/src/com/cyanogenmod/filemanager/commands/ListExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ListExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.FileSystemObject;
+import me.toolify.backbone.model.FileSystemObject;
import java.util.List;
diff --git a/src/com/cyanogenmod/filemanager/commands/MountExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/MountExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/MountExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/MountExecutable.java
index 1e6b2ca17..4d5c41ad6 100644
--- a/src/com/cyanogenmod/filemanager/commands/MountExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/MountExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for mount filesystems.
diff --git a/src/com/cyanogenmod/filemanager/commands/MountPointInfoExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/MountPointInfoExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/MountPointInfoExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/MountPointInfoExecutable.java
index af0ce5e58..8806ab047 100644
--- a/src/com/cyanogenmod/filemanager/commands/MountPointInfoExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/MountPointInfoExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.MountPoint;
+import me.toolify.backbone.model.MountPoint;
import java.util.List;
diff --git a/src/com/cyanogenmod/filemanager/commands/MoveExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/MoveExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/MoveExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/MoveExecutable.java
index 4d9bb2a15..12ea61d88 100644
--- a/src/com/cyanogenmod/filemanager/commands/MoveExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/MoveExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for move a file system object to
diff --git a/src/com/cyanogenmod/filemanager/commands/ParentDirExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ParentDirExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/ParentDirExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ParentDirExecutable.java
index 71f1ea7bb..c65ccbd2d 100644
--- a/src/com/cyanogenmod/filemanager/commands/ParentDirExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ParentDirExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for retrieve the parent directory of
diff --git a/src/com/cyanogenmod/filemanager/commands/ProcessIdExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ProcessIdExecutable.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/ProcessIdExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ProcessIdExecutable.java
index cbcbce3c7..bdc1e1524 100644
--- a/src/com/cyanogenmod/filemanager/commands/ProcessIdExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ProcessIdExecutable.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
+
+import java.util.List;
/**
* An interface that represents an executable for retrieve the process identifier
@@ -26,5 +28,5 @@ public interface ProcessIdExecutable extends SyncResultExecutable {
* {@inheritDoc}
*/
@Override
- Integer getResult();
+ List getResult();
}
diff --git a/src/com/cyanogenmod/filemanager/commands/QuickFolderSearchExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/QuickFolderSearchExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/QuickFolderSearchExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/QuickFolderSearchExecutable.java
index 54b0bda2d..8b3058e03 100644
--- a/src/com/cyanogenmod/filemanager/commands/QuickFolderSearchExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/QuickFolderSearchExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
import java.util.List;
diff --git a/src/com/cyanogenmod/filemanager/commands/ReadExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ReadExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/ReadExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ReadExecutable.java
index 475eb5c1f..3f1ab6b1e 100644
--- a/src/com/cyanogenmod/filemanager/commands/ReadExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ReadExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for read files.
diff --git a/src/com/cyanogenmod/filemanager/commands/ResolveLinkExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/ResolveLinkExecutable.java
similarity index 89%
rename from src/com/cyanogenmod/filemanager/commands/ResolveLinkExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/ResolveLinkExecutable.java
index a5a2e6385..48bf851c7 100644
--- a/src/com/cyanogenmod/filemanager/commands/ResolveLinkExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/ResolveLinkExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.FileSystemObject;
+import me.toolify.backbone.model.FileSystemObject;
/**
* An interface that represents an executable for resolves the real
diff --git a/src/com/cyanogenmod/filemanager/commands/SIGNAL.java b/Backbone/src/main/java/me/toolify/backbone/commands/SIGNAL.java
similarity index 97%
rename from src/com/cyanogenmod/filemanager/commands/SIGNAL.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/SIGNAL.java
index 26bcaa03d..cb8b7498e 100644
--- a/src/com/cyanogenmod/filemanager/commands/SIGNAL.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/SIGNAL.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An enumeration of allow signals that can send to programs.
diff --git a/src/com/cyanogenmod/filemanager/commands/SendSignalExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/SendSignalExecutable.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/SendSignalExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/SendSignalExecutable.java
index 076f70987..b280dfcd8 100644
--- a/src/com/cyanogenmod/filemanager/commands/SendSignalExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/SendSignalExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for send signal to processes.
diff --git a/src/com/cyanogenmod/filemanager/commands/SyncResultExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/SyncResultExecutable.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/SyncResultExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/SyncResultExecutable.java
index bfe51f6c0..a4371bde4 100644
--- a/src/com/cyanogenmod/filemanager/commands/SyncResultExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/SyncResultExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that defines a class as executable in a synchronous way.
diff --git a/src/com/cyanogenmod/filemanager/commands/UncompressExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/UncompressExecutable.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/commands/UncompressExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/UncompressExecutable.java
index 3a2b21cc7..22587bc3e 100644
--- a/src/com/cyanogenmod/filemanager/commands/UncompressExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/UncompressExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
/**
* An interface that represents an executable for uncompress file system objects.
diff --git a/src/com/cyanogenmod/filemanager/commands/WritableExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/WritableExecutable.java
similarity index 92%
rename from src/com/cyanogenmod/filemanager/commands/WritableExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/WritableExecutable.java
index f00702a65..920020799 100644
--- a/src/com/cyanogenmod/filemanager/commands/WritableExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/WritableExecutable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
-import com.cyanogenmod.filemanager.model.MountPoint;
+import me.toolify.backbone.model.MountPoint;
/**
* An interface that represents an executable that writes in a filesystem.
diff --git a/src/com/cyanogenmod/filemanager/commands/WriteExecutable.java b/Backbone/src/main/java/me/toolify/backbone/commands/WriteExecutable.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/commands/WriteExecutable.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/WriteExecutable.java
index 1b4bc4b5f..b4621bc61 100644
--- a/src/com/cyanogenmod/filemanager/commands/WriteExecutable.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/WriteExecutable.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands;
+package me.toolify.backbone.commands;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/ChecksumCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/ChecksumCommand.java
new file mode 100644
index 000000000..68b237adc
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/ChecksumCommand.java
@@ -0,0 +1,273 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ChecksumExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.util.HexDump;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.util.Locale;
+
+/**
+ * A class for calculate MD5 and SHA-1 checksums of a file system object.
+ *
+ * Partial results are returned in order (MD5 -> SHA1)
+ */
+public class ChecksumCommand extends Program implements ChecksumExecutable {
+
+ private static final String TAG = "ChecksumCommand"; //$NON-NLS-1$
+
+ private final File mSrc;
+ private final String[] mChecksums;
+ private final AsyncResultListener mAsyncResultListener;
+
+ private boolean mCancelled;
+ private final Object mSync = new Object();
+
+ /**
+ * Constructor of ChecksumCommand.
+ *
+ * @param src The source file
+ * @param asyncResultListener The partial result listener
+ */
+ public ChecksumCommand(
+ String src, AsyncResultListener asyncResultListener) {
+ super();
+ this.mAsyncResultListener = asyncResultListener;
+ this.mChecksums = new String[]{null, null};
+ this.mSrc = new File(src);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute() throws InsufficientPermissionsException,
+ NoSuchFileOrDirectory, ExecutionException {
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Calculating checksums of file %s", this.mSrc)); //$NON-NLS-1$
+ }
+
+ // Check that the file exists
+ if (!this.mSrc.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mSrc.getAbsolutePath());
+ }
+
+ CHECKSUMS checksum = CHECKSUMS.MD5;
+ try {
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ // Calculate digests
+ calculateDigest(checksum);
+ checksum = CHECKSUMS.SHA1;
+ calculateDigest(checksum);
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(false);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+
+ } catch (InterruptedException ie) {
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(true);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(143);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: CANCELLED"); //$NON-NLS-1$
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG,
+ String.format(
+ "Fail to calculate %s checksum of file %s", //$NON-NLS-1$
+ checksum.name(),
+ this.mSrc.getAbsolutePath()),
+ e);
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(e);
+ }
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL"); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ try {
+ synchronized (this.mSync) {
+ this.mCancelled = true;
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ return cancel();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getResult() {
+ return this.mChecksums;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChecksum(CHECKSUMS checksum) {
+ return getResult()[checksum.ordinal()];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+
+ /**
+ * Method that calculate a digest of the file for the source file
+ *
+ * @param type The type of digest to obtain
+ * @throws InterruptedException If the operation was cancelled
+ * @throws Exception If an error occurs
+ */
+ private void calculateDigest(CHECKSUMS type) throws InterruptedException, Exception {
+
+ InputStream is = null;
+ try {
+ MessageDigest md = MessageDigest.getInstance(type.name());
+ is = new FileInputStream(this.mSrc);
+
+ // Start digesting
+ byte[] data = new byte[getBufferSize()];
+ int read = 0;
+ while ((read = is.read(data, 0, getBufferSize())) != -1) {
+ checkCancelled();
+ md.update(data, 0, read);
+ }
+ checkCancelled();
+
+ // Finally digest
+ this.mChecksums[type.ordinal()] =
+ HexDump.toHexString(md.digest()).toLowerCase(Locale.ROOT);
+ checkCancelled();
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onPartialResult(this.mChecksums[type.ordinal()]);
+ }
+
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * Checks if the operation was cancelled
+ *
+ * @throws InterruptedException If the operation was cancelled
+ */
+ private void checkCancelled() throws InterruptedException {
+ synchronized (this.mSync) {
+ if (this.mCancelled) {
+ throw new InterruptedException();
+ }
+ }
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/CopyCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/CopyCommand.java
new file mode 100644
index 000000000..7b272ca5b
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/CopyCommand.java
@@ -0,0 +1,111 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.CopyExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+
+
+/**
+ * A class for copy a file or directory.
+ */
+public class CopyCommand extends Program implements CopyExecutable {
+
+ private static final String TAG = "CopyCommand"; //$NON-NLS-1$
+
+ private final String mSrc;
+ private final String mDst;
+
+ /**
+ * Constructor of CopyCommand.
+ *
+ * @param src The name of the file or directory to be copied
+ * @param dst The name of the file or directory in which copy the source file or directory
+ */
+ public CopyCommand(String src, String dst) {
+ super();
+ this.mSrc = src;
+ this.mDst = dst;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Moving from %s to %s", //$NON-NLS-1$
+ this.mSrc, this.mDst));
+ }
+
+ File s = new File(this.mSrc);
+ File d = new File(this.mDst);
+ if (!s.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mSrc);
+ }
+
+ //Copy recursively
+ if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mDst);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateDirCommand.java
new file mode 100644
index 000000000..6f6f3d695
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateDirCommand.java
@@ -0,0 +1,109 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.CreateDirExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+
+
+/**
+ * A class for create a directory.
+ */
+public class CreateDirCommand extends Program implements CreateDirExecutable {
+
+ private static final String TAG = "CreateDirCommand"; //$NON-NLS-1$
+
+ private final String mPath;
+
+ /**
+ * Constructor of CreateDirCommand.
+ *
+ * @param path The name of the new directory
+ */
+ public CreateDirCommand(String path) {
+ super();
+ this.mPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Creating directory: %s", this.mPath)); //$NON-NLS-1$
+ }
+
+ File f = new File(this.mPath);
+ // Check that if the path exist, it need to be a directory. Otherwise something is
+ // wrong
+ if (f.exists() && !f.isDirectory()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. ExecutionException"); //$NON-NLS-1$
+ }
+ throw new ExecutionException("the path exists but is not a folder"); //$NON-NLS-1$
+ }
+
+ // Only create the directory if the folder not exists. Otherwise mkdir will return false
+ if (!f.exists()) {
+ if (!f.mkdir()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mPath);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateFileCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateFileCommand.java
new file mode 100644
index 000000000..bb825f91f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/CreateFileCommand.java
@@ -0,0 +1,119 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.CreateFileExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+import java.io.IOException;
+
+
+/**
+ * A class for create a file.
+ */
+public class CreateFileCommand extends Program implements CreateFileExecutable {
+
+ private static final String TAG = "CreateFileCommand"; //$NON-NLS-1$
+
+
+ private final String mPath;
+
+ /**
+ * Constructor of CreateFileCommand.
+ *
+ * @param path The name of the new file
+ */
+ public CreateFileCommand(String path) {
+ super();
+ this.mPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Creating file: %s", this.mPath)); //$NON-NLS-1$
+ }
+
+ File f = new File(this.mPath);
+ try {
+ // Check that if the path exist, it need to be a file. Otherwise something is
+ // wrong
+ if (f.exists() && !f.isFile()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. ExecutionException"); //$NON-NLS-1$
+ }
+ throw new ExecutionException("the path exists but is not a file"); //$NON-NLS-1$
+ }
+
+ // Only create the file if the file not exists. Otherwise createNewFile
+ // will return false
+ if (!f.exists()) {
+ if (!f.createNewFile()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+ }
+ } catch (IOException ioe) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mPath);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteDirCommand.java
new file mode 100644
index 000000000..b7283f5b7
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteDirCommand.java
@@ -0,0 +1,115 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.DeleteDirExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+
+
+/**
+ * A class for delete a folder.
+ */
+public class DeleteDirCommand extends Program implements DeleteDirExecutable {
+
+ private static final String TAG = "DeleteDirCommand"; //$NON-NLS-1$
+
+ private final String mPath;
+
+ /**
+ * Constructor of DeleteDirCommand.
+ *
+ * @param path The name of the new folder
+ */
+ public DeleteDirCommand(String path) {
+ super();
+ this.mPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Deleting directory: %s", this.mPath)); //$NON-NLS-1$
+ }
+
+ File f = new File(this.mPath);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mPath);
+ }
+
+ // Check that if the path exist, it need to be a folder. Otherwise something is
+ // wrong
+ if (f.exists() && !f.isDirectory()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. ExecutionException"); //$NON-NLS-1$
+ }
+ throw new ExecutionException("the path exists but is not a folder"); //$NON-NLS-1$
+ }
+
+ // Delete the file
+ if (!FileHelper.deleteFolder(f)) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mPath);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteFileCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteFileCommand.java
new file mode 100644
index 000000000..ccd978924
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/DeleteFileCommand.java
@@ -0,0 +1,114 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.DeleteFileExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+
+
+/**
+ * A class for delete a file.
+ */
+public class DeleteFileCommand extends Program implements DeleteFileExecutable {
+
+ private static final String TAG = "DeleteFileCommand"; //$NON-NLS-1$
+
+ private final String mPath;
+
+ /**
+ * Constructor of DeleteFileCommand.
+ *
+ * @param path The name of the new file
+ */
+ public DeleteFileCommand(String path) {
+ super();
+ this.mPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Deleting file: %s", this.mPath)); //$NON-NLS-1$
+ }
+
+ File f = new File(this.mPath);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mPath);
+ }
+
+ // Check that if the path exist, it need to be a file. Otherwise something is
+ // wrong
+ if (f.exists() && !f.isFile()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. ExecutionException"); //$NON-NLS-1$
+ }
+ throw new ExecutionException("the path exists but is not a file"); //$NON-NLS-1$
+ }
+
+ // Delete the file
+ if (!f.delete()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mPath);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/DiskUsageCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/DiskUsageCommand.java
new file mode 100644
index 000000000..cad2730ea
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/DiskUsageCommand.java
@@ -0,0 +1,134 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.BuildConfig;
+import me.toolify.backbone.commands.DiskUsageExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.DiskUsage;
+import me.toolify.backbone.model.MountPoint;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+
+/**
+ * A class for get information about disk usage.
+ */
+public class DiskUsageCommand extends Program implements DiskUsageExecutable {
+
+ private static final String TAG = "DiskUsageCommand"; //$NON-NLS-1$
+
+ private final String mMountsFile;
+ private final String mSrc;
+ private final Hashtable mDisksUsage = new Hashtable();
+
+ /**
+ * Constructor of DiskUsageCommand.
+ *
+ * @param mountsFile The system mounts file
+ */
+ public DiskUsageCommand(String mountsFile) {
+ this(mountsFile, null);
+ }
+
+ /**
+ * Constructor of DiskUsageCommand.
+ *
+ * @param mountsFile The system mounts file
+ * @param dir The directory of which obtain its disk usage
+ */
+ public DiskUsageCommand(String mountsFile, String dir) {
+ super();
+ this.mMountsFile = mountsFile;
+ this.mSrc = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ List ret = new ArrayList();
+ for(DiskUsage du : mDisksUsage.values())
+ ret.add(du);
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Getting usage for: %s", //$NON-NLS-1$
+ this.mSrc == null ? "all" : this.mSrc)); //$NON-NLS-1$
+ }
+
+ if (this.mSrc == null) {
+ // Retrieve the mount points
+ MountPointInfoCommand cmd = new MountPointInfoCommand(this.mMountsFile);
+ cmd.setBufferSize(getBufferSize());
+ cmd.setTrace(isTrace());
+ cmd.execute();
+ List mp = cmd.getResult();
+
+ // Get every disk usage
+ for (int i = 0; i < mp.size(); i++) {
+ String mpp = mp.get(i).getMountPoint();
+ File root = new File(mpp);
+ mDisksUsage.put(mpp, createDiskUsage(root));
+ }
+ } else {
+ mDisksUsage.put(mSrc, createDiskUsage(new File(this.mSrc)));
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Method that create a reference of the disk usage from the root file
+ *
+ * @param root The root file
+ * @return DiskUsage The disk usage
+ */
+ private DiskUsage createDiskUsage(File root) {
+ long total = root.getTotalSpace();
+ long free = root.getFreeSpace();
+ DiskUsage du = new DiskUsage(
+ root.getAbsolutePath(),
+ total,
+ total - free,
+ free);
+ if (isTrace()) {
+ Log.v(TAG, du.toString());
+ }
+ return du;
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/FindCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/FindCommand.java
new file mode 100644
index 000000000..f253c3197
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/FindCommand.java
@@ -0,0 +1,255 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.FindExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.SearchHelper;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * A class for search files.
+ */
+public class FindCommand extends Program implements FindExecutable {
+
+ private static final String TAG = "FindCommand"; //$NON-NLS-1$
+
+ private final String mDirectory;
+ private final String[] mQueryRegExp;
+ private final AsyncResultListener mAsyncResultListener;
+
+ private boolean mCancelled;
+ private boolean mEnded;
+ private final Object mSync = new Object();
+
+ /**
+ * Constructor of FindCommand.
+ *
+ * @param directory The absolute directory where start the search
+ * @param query The terms to be searched
+ * @param asyncResultListener The partial result listener
+ */
+ public FindCommand(String directory, Query query, AsyncResultListener asyncResultListener) {
+ super();
+ this.mDirectory = directory;
+ this.mQueryRegExp = createRegexp(directory, query);
+ this.mAsyncResultListener = asyncResultListener;
+ this.mCancelled = false;
+ this.mEnded = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Finding in %s the query %s", //$NON-NLS-1$
+ this.mDirectory, Arrays.toString(this.mQueryRegExp)));
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ File f = new File(this.mDirectory);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(new NoSuchFileOrDirectory(this.mDirectory));
+ }
+ }
+ if (!f.isDirectory()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(
+ new ExecutionException("path exists but it's not a folder")); //$NON-NLS-1$
+ }
+ }
+
+ // Find the data
+ findRecursive(f);
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(this.mCancelled);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Method that search files recursively
+ *
+ * @param folder The folder where to start the search
+ */
+ private void findRecursive(File folder) {
+ // Obtains the files and folders of the folders
+ File[] files = folder.listFiles();
+ if (files != null) {
+ int cc = files.length;
+ for (int i = 0; i < cc; i++) {
+ if (files[i].isDirectory()) {
+ findRecursive(files[i]);
+ }
+
+ // Check if the file or folder matches the regexp
+ try {
+ int ccc = this.mQueryRegExp.length;
+ for (int j = 0; j < ccc; j++) {
+ if (files[i].getName().matches(this.mQueryRegExp[j])) {
+ FileSystemObject fso =
+ FileHelper.createFileSystemObject(files[i]);
+ if (fso != null) {
+ if (isTrace()) {
+ Log.v(TAG, String.valueOf(fso));
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onPartialResult(fso);
+ }
+ }
+ }
+ }
+ } catch (Exception e) {/**NON-BLOCK**/}
+
+ // Check if the process was cancelled
+ try {
+ synchronized (this.mSync) {
+ if (this.mCancelled || this.mEnded) {
+ this.mSync.notify();
+ break;
+ }
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ try {
+ synchronized (this.mSync) {
+ this.mCancelled = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ try {
+ synchronized (this.mSync) {
+ this.mEnded = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+
+ /**
+ * Method that create the regexp of this command, using the directory and
+ * arguments and creating the regular expressions of the search.
+ *
+ * @param directory The directory where to search
+ * @param query The query make for user
+ * @return String[] The regexp for filtering files
+ */
+ private static String[] createRegexp(String directory, Query query) {
+ String[] args = new String[query.getSlotsCount()];
+ int cc = query.getSlotsCount();
+ for (int i = 0; i < cc; i++) {
+ args[i] = SearchHelper.toIgnoreCaseRegExp(query.getSlot(i), true);
+ }
+ return args;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/FolderUsageCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/FolderUsageCommand.java
new file mode 100644
index 000000000..74ffd4384
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/FolderUsageCommand.java
@@ -0,0 +1,258 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.FolderUsageExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.FolderUsage;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.MimeTypeHelper.MimeTypeCategory;
+
+import java.io.File;
+
+/**
+ * A class for retrieve the disk usage of a folder.
+ */
+public class FolderUsageCommand extends Program implements FolderUsageExecutable {
+
+ private static final String TAG = "FolderUsage"; //$NON-NLS-1$
+
+ private final String mDirectory;
+ private final AsyncResultListener mAsyncResultListener;
+ private final FolderUsage mFolderUsage;
+
+ private boolean mCancelled;
+ private boolean mEnded;
+ private final Object mSync = new Object();
+
+ /**
+ * Constructor of FolderUsageCommand.
+ *
+ * @param directory The absolute directory to compute
+ * @param asyncResultListener The partial result listener
+ */
+ public FolderUsageCommand(
+ String directory, AsyncResultListener asyncResultListener) {
+ super();
+ this.mDirectory = directory;
+ this.mAsyncResultListener = asyncResultListener;
+ this.mFolderUsage = new FolderUsage(directory);
+ this.mCancelled = false;
+ this.mEnded = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FolderUsage getFolderUsage() {
+ return this.mFolderUsage;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Computing folder usage for folder %s", //$NON-NLS-1$
+ this.mDirectory));
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ File f = new File(this.mDirectory);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(new NoSuchFileOrDirectory(this.mDirectory));
+ }
+ }
+ if (!f.isDirectory()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(
+ new ExecutionException("path exists but it's not a folder")); //$NON-NLS-1$
+ }
+ }
+
+ // Compute data recursively
+ computeRecursive(f);
+
+ synchronized (this.mSync) {
+ this.mEnded = true;
+ this.mSync.notify();
+ }
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(this.mCancelled);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Method that computes the folder usage recursively
+ *
+ * @param folder The folder where to start the computation
+ */
+ private void computeRecursive(File folder) {
+ // Obtains the files and folders of the folders
+ try {
+ File[] files = folder.listFiles();
+ int c = 0;
+ if (files != null) {
+ int cc = files.length;
+ for (int i = 0; i < cc; i++) {
+ if (files[i].isDirectory()) {
+ this.mFolderUsage.addFolder();
+ computeRecursive(files[i]);
+ } else {
+ this.mFolderUsage.addFile();
+ // Compute statistics and size
+ MimeTypeCategory category =
+ MimeTypeHelper.getCategory(null, files[i]);
+ this.mFolderUsage.addFileToCategory(category);
+ this.mFolderUsage.addSize(files[i].length());
+ }
+
+ // Partial notification
+ if (c % 5 == 0) {
+ //If a listener is defined, then send the partial result
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(this.mFolderUsage);
+ }
+ }
+
+ // Check if the process was cancelled
+ try {
+ synchronized (this.mSync) {
+ if (this.mCancelled || this.mEnded) {
+ this.mSync.notify();
+ break;
+ }
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+ } finally {
+ //If a listener is defined, then send the partial result
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(this.mFolderUsage);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ try {
+ synchronized (this.mSync) {
+ if (this.mEnded || this.mCancelled) {
+ this.mCancelled = true;
+ return true;
+ }
+ this.mCancelled = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ try {
+ synchronized (this.mSync) {
+ this.mEnded = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableCreator.java
similarity index 76%
rename from src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableCreator.java
index 875544efe..9a5e5efda 100644
--- a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableCreator.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableCreator.java
@@ -14,51 +14,50 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.java;
-
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.commands.ChangeCurrentDirExecutable;
-import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
-import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
-import com.cyanogenmod.filemanager.commands.CompressExecutable;
-import com.cyanogenmod.filemanager.commands.CopyExecutable;
-import com.cyanogenmod.filemanager.commands.CreateDirExecutable;
-import com.cyanogenmod.filemanager.commands.CreateFileExecutable;
-import com.cyanogenmod.filemanager.commands.CurrentDirExecutable;
-import com.cyanogenmod.filemanager.commands.DeleteDirExecutable;
-import com.cyanogenmod.filemanager.commands.DeleteFileExecutable;
-import com.cyanogenmod.filemanager.commands.DiskUsageExecutable;
-import com.cyanogenmod.filemanager.commands.EchoExecutable;
-import com.cyanogenmod.filemanager.commands.ExecExecutable;
-import com.cyanogenmod.filemanager.commands.ExecutableCreator;
-import com.cyanogenmod.filemanager.commands.FindExecutable;
-import com.cyanogenmod.filemanager.commands.FolderUsageExecutable;
-import com.cyanogenmod.filemanager.commands.GroupsExecutable;
-import com.cyanogenmod.filemanager.commands.IdentityExecutable;
-import com.cyanogenmod.filemanager.commands.LinkExecutable;
-import com.cyanogenmod.filemanager.commands.ListExecutable;
-import com.cyanogenmod.filemanager.commands.ListExecutable.LIST_MODE;
-import com.cyanogenmod.filemanager.commands.MountExecutable;
-import com.cyanogenmod.filemanager.commands.MountPointInfoExecutable;
-import com.cyanogenmod.filemanager.commands.MoveExecutable;
-import com.cyanogenmod.filemanager.commands.ParentDirExecutable;
-import com.cyanogenmod.filemanager.commands.ProcessIdExecutable;
-import com.cyanogenmod.filemanager.commands.QuickFolderSearchExecutable;
-import com.cyanogenmod.filemanager.commands.ReadExecutable;
-import com.cyanogenmod.filemanager.commands.ResolveLinkExecutable;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.commands.SendSignalExecutable;
-import com.cyanogenmod.filemanager.commands.UncompressExecutable;
-import com.cyanogenmod.filemanager.commands.WriteExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.java.JavaConsole;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.model.Permissions;
-import com.cyanogenmod.filemanager.model.Query;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.preferences.CompressionMode;
+package me.toolify.backbone.commands.java;
+
+import me.toolify.backbone.R;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ChangeOwnerExecutable;
+import me.toolify.backbone.commands.ChangePermissionsExecutable;
+import me.toolify.backbone.commands.ChecksumExecutable;
+import me.toolify.backbone.commands.CompressExecutable;
+import me.toolify.backbone.commands.CopyExecutable;
+import me.toolify.backbone.commands.CreateDirExecutable;
+import me.toolify.backbone.commands.CreateFileExecutable;
+import me.toolify.backbone.commands.DeleteDirExecutable;
+import me.toolify.backbone.commands.DeleteFileExecutable;
+import me.toolify.backbone.commands.DiskUsageExecutable;
+import me.toolify.backbone.commands.EchoExecutable;
+import me.toolify.backbone.commands.ExecExecutable;
+import me.toolify.backbone.commands.ExecutableCreator;
+import me.toolify.backbone.commands.FindExecutable;
+import me.toolify.backbone.commands.FolderUsageExecutable;
+import me.toolify.backbone.commands.GroupsExecutable;
+import me.toolify.backbone.commands.IdentityExecutable;
+import me.toolify.backbone.commands.LinkExecutable;
+import me.toolify.backbone.commands.ListExecutable;
+import me.toolify.backbone.commands.ListExecutable.LIST_MODE;
+import me.toolify.backbone.commands.MountExecutable;
+import me.toolify.backbone.commands.MountPointInfoExecutable;
+import me.toolify.backbone.commands.MoveExecutable;
+import me.toolify.backbone.commands.ParentDirExecutable;
+import me.toolify.backbone.commands.ProcessIdExecutable;
+import me.toolify.backbone.commands.QuickFolderSearchExecutable;
+import me.toolify.backbone.commands.ReadExecutable;
+import me.toolify.backbone.commands.ResolveLinkExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.commands.SendSignalExecutable;
+import me.toolify.backbone.commands.UncompressExecutable;
+import me.toolify.backbone.commands.WriteExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.java.JavaConsole;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.model.Permissions;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.model.User;
+import me.toolify.backbone.preferences.CompressionMode;
/**
* A class for create shell {@link "Executable"} objects.
@@ -77,15 +76,6 @@ public class JavaExecutableCreator implements ExecutableCreator {
this.mConsole = console;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public ChangeCurrentDirExecutable createChangeCurrentDirExecutable(String dir)
- throws CommandNotFoundException {
- return new ChangeCurrentDirCommand(this.mConsole, dir);
- }
-
/**
* {@inheritDoc}
*/
@@ -131,14 +121,6 @@ public CreateFileExecutable createCreateFileExecutable(String file)
return new CreateFileCommand(file);
}
- /**
- * {@inheritDoc}
- */
- @Override
- public CurrentDirExecutable createCurrentDirExecutable() throws CommandNotFoundException {
- return new CurrentDirCommand(this.mConsole);
- }
-
/**
* {@inheritDoc}
*/
@@ -302,6 +284,15 @@ public ProcessIdExecutable createShellProcessIdExecutable() throws CommandNotFou
throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ProcessIdExecutable createProcessIdExecutable(int pid)
+ throws CommandNotFoundException {
+ throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
+ }
+
/**
* {@inheritDoc}
*/
@@ -400,4 +391,14 @@ public UncompressExecutable createUncompressExecutable(
throw new CommandNotFoundException("Not implemented"); //$NON-NLS-1$
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException {
+ return new ChecksumCommand(src, asyncResultListener);
+ }
+
}
diff --git a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableFactory.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableFactory.java
similarity index 83%
rename from src/com/cyanogenmod/filemanager/commands/java/JavaExecutableFactory.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableFactory.java
index 0220e0ff7..a474ba00f 100644
--- a/src/com/cyanogenmod/filemanager/commands/java/JavaExecutableFactory.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/JavaExecutableFactory.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.java;
+package me.toolify.backbone.commands.java;
-import com.cyanogenmod.filemanager.commands.ExecutableCreator;
-import com.cyanogenmod.filemanager.commands.ExecutableFactory;
-import com.cyanogenmod.filemanager.console.java.JavaConsole;
+import me.toolify.backbone.commands.ExecutableCreator;
+import me.toolify.backbone.commands.ExecutableFactory;
+import me.toolify.backbone.console.java.JavaConsole;
/**
* A class that represents a factory for creating java {@link "Executable"} objects.
*/
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/ListCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/ListCommand.java
new file mode 100644
index 000000000..f58b3c572
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/ListCommand.java
@@ -0,0 +1,132 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.ListExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.ParentDirectory;
+import me.toolify.backbone.util.FileHelper;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for list information about files and directories.
+ */
+public class ListCommand extends Program implements ListExecutable {
+
+ private static final String TAG = "ListCommand"; //$NON-NLS-1$
+
+ private final String mSrc;
+ private final LIST_MODE mMode;
+ private final List mFiles;
+
+ /**
+ * Constructor of ListCommand. List mode.
+ *
+ * @param src The file system object to be listed
+ * @param mode The mode of listing
+ */
+ public ListCommand(String src, LIST_MODE mode) {
+ super();
+ this.mSrc = src;
+ this.mMode = mode;
+ this.mFiles = new ArrayList();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mFiles;
+ }
+
+ /**
+ * Method that returns a single result of the program invocation.
+ * Only must be called within a FILEINFO mode listing.
+ *
+ * @return FileSystemObject The file system object reference
+ */
+ public FileSystemObject getSingleResult() {
+ return this.mFiles.get(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Listing %s. Mode: %s", //$NON-NLS-1$
+ this.mSrc, this.mMode));
+ }
+
+ File f = new File(this.mSrc);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mSrc);
+ }
+ if (this.mMode.compareTo(LIST_MODE.DIRECTORY) == 0) {
+ File[] files = f.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ FileSystemObject fso = FileHelper.createFileSystemObject(files[i]);
+ if (fso != null) {
+ if (isTrace()) {
+ Log.v(TAG, String.valueOf(fso));
+ }
+ this.mFiles.add(fso);
+ }
+ }
+ }
+
+ //Now if not is the root directory
+ if (this.mSrc != null &&
+ this.mSrc.compareTo(FileHelper.ROOT_DIRECTORY) != 0 &&
+ this.mMode.compareTo(LIST_MODE.DIRECTORY) == 0) {
+ this.mFiles.add(0, new ParentDirectory(new File(this.mSrc).getParent()));
+ }
+
+ } else {
+ // Build the parent information
+ FileSystemObject fso = FileHelper.createFileSystemObject(f);
+ if (fso != null) {
+ if (isTrace()) {
+ Log.v(TAG, String.valueOf(fso));
+ }
+ this.mFiles.add(fso);
+ }
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/MountPointInfoCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/MountPointInfoCommand.java
new file mode 100644
index 000000000..c0b4fcfd9
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/MountPointInfoCommand.java
@@ -0,0 +1,117 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.MountPointInfoExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for get information about disk usage.
+ */
+public class MountPointInfoCommand extends Program implements MountPointInfoExecutable {
+
+ private static final String TAG = "MountPointInfoCommand"; //$NON-NLS-1$
+
+ private final String mMountsFile;
+ private final List mMountPoints;
+
+ /**
+ * Constructor of MountPointInfoCommand.
+ *
+ * @param mountsFile The system mounts file
+ */
+ public MountPointInfoCommand(String mountsFile) {
+ super();
+ this.mMountPoints = new ArrayList();
+ this.mMountsFile = mountsFile;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mMountPoints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Getting usage from %s", //$NON-NLS-1$
+ this.mMountsFile));
+ }
+
+ // Read the file with the mount information
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(new File(this.mMountsFile)), getBufferSize());
+ StringBuilder sb = new StringBuilder();
+ int read = 0;
+ char[] data = new char[getBufferSize()];
+ while ((read = br.read(data, 0, getBufferSize())) != -1) {
+ sb.append(data, 0, read);
+ }
+
+ // Send to parse
+ String[] lines = sb.toString().split("\n"); //$NON-NLS-1$
+ for (int i = 0; i < lines.length; i++) {
+ MountPoint mp = ParseHelper.toMountPoint(lines[i]);
+ if (isTrace()) {
+ Log.v(TAG, String.valueOf(mp));
+ }
+ this.mMountPoints.add(mp);
+ }
+
+ } catch (Exception e) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/MoveCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/MoveCommand.java
new file mode 100644
index 000000000..e7f1ecf12
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/MoveCommand.java
@@ -0,0 +1,133 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.MoveExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.io.File;
+
+
+/**
+ * A class for move a file or directory.
+ */
+public class MoveCommand extends Program implements MoveExecutable {
+
+ private static final String TAG = "MoveCommand"; //$NON-NLS-1$
+
+ private final String mSrc;
+ private final String mDst;
+
+ /**
+ * Constructor of MoveCommand.
+ *
+ * @param src The name of the file or directory to be moved
+ * @param dst The name of the file or directory in which move the source file or directory
+ */
+ public MoveCommand(String src, String dst) {
+ super();
+ this.mSrc = src;
+ this.mDst = dst;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Creating from %s to %s", this.mSrc, this.mDst)); //$NON-NLS-1$
+ }
+
+ File s = new File(this.mSrc);
+ File d = new File(this.mDst);
+ if (!s.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ throw new NoSuchFileOrDirectory(this.mSrc);
+ }
+
+ //Move or copy recursively
+ if (d.exists()) {
+ if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+ if (!FileHelper.deleteFolder(s)) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK. WARNING. Source not deleted."); //$NON-NLS-1$
+ }
+ }
+ } else {
+ // Move between filesystem is not allow. If rename fails then use copy operation
+ if (!s.renameTo(d)) {
+ if (!FileHelper.copyRecursive(s, d, getBufferSize())) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+ if (!FileHelper.deleteFolder(s)) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK. WARNING. Source not deleted."); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mSrc);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mDst);
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/ParentDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/ParentDirCommand.java
new file mode 100644
index 000000000..ea9fec57d
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/ParentDirCommand.java
@@ -0,0 +1,83 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.ParentDirExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+
+import java.io.File;
+
+
+/**
+ * A class for returns the parent directory.
+ */
+public class ParentDirCommand extends Program implements ParentDirExecutable {
+
+ private static final String TAG = "ParentDirCommand"; //$NON-NLS-1$
+
+ private final String mSrc;
+ private String mParentDir;
+
+ /**
+ * Constructor of ParentDirCommand.
+ *
+ * @param src The source file
+ */
+ public ParentDirCommand(String src) {
+ super();
+ this.mSrc = src;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getResult() {
+ return this.mParentDir;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Getting parent directory of %s", //$NON-NLS-1$
+ this.mSrc));
+ }
+
+ File f = new File(this.mSrc);
+ this.mParentDir = f.getParent();
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Parent directory: %S", //$NON-NLS-1$
+ this.mParentDir));
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/Program.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/Program.java
new file mode 100644
index 000000000..b017d8bc8
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/Program.java
@@ -0,0 +1,97 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import me.toolify.backbone.commands.Executable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+
+
+/**
+ * An abstract base class for all java executables.
+ */
+public abstract class Program implements Executable {
+
+ private boolean mTrace;
+ private int mBufferSize;
+
+ /**
+ * Constructor of Program
+ */
+ public Program() {
+ super();
+ }
+
+ /**
+ * Method that return if the command has to trace his operations
+ *
+ * @return boolean If the command has to trace
+ */
+ public boolean isTrace() {
+ return this.mTrace;
+ }
+
+ /**
+ * Method that sets if the command has to trace his operations
+ *
+ * @param trace If the command has to trace
+ */
+ public void setTrace(boolean trace) {
+ this.mTrace = trace;
+ }
+
+ /**
+ * Method that return the buffer size of the program
+ *
+ * @return int The buffer size of the program
+ */
+ public int getBufferSize() {
+ return this.mBufferSize;
+ }
+
+ /**
+ * Method that sets the buffer size of the program
+ *
+ * @param bufferSize The buffer size of the program
+ */
+ public void setBufferSize(int bufferSize) {
+ this.mBufferSize = bufferSize;
+ }
+
+ /**
+ * Method that returns if this program uses an asynchronous model. false
+ * by default.
+ *
+ * @return boolean If this program uses an asynchronous model
+ */
+ @SuppressWarnings("static-method")
+ public boolean isAsynchronous() {
+ return false;
+ }
+
+ /**
+ * Method that executes the program
+ *
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws ExecutionException If the operation returns a invalid exit code
+ */
+ public abstract void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException;
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/ReadCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/ReadCommand.java
new file mode 100644
index 000000000..464930d3e
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/ReadCommand.java
@@ -0,0 +1,233 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ReadExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+/**
+ * A class for read a file.
+ */
+public class ReadCommand extends Program implements ReadExecutable {
+
+ private static final String TAG = "ReadCommand"; //$NON-NLS-1$
+
+ private final String mFile;
+ private final AsyncResultListener mAsyncResultListener;
+
+ private boolean mCancelled;
+ private boolean mEnded;
+ private final Object mSync = new Object();
+
+ /**
+ * Constructor of ExecCommand.
+ *
+ * @param file The file to read
+ * @param asyncResultListener The partial result listener
+ */
+ public ReadCommand(
+ String file, AsyncResultListener asyncResultListener) {
+ super();
+ this.mFile = file;
+ this.mAsyncResultListener = asyncResultListener;
+ this.mCancelled = false;
+ this.mEnded = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Reading file %s", this.mFile)); //$NON-NLS-1$
+
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ File f = new File(this.mFile);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(new NoSuchFileOrDirectory(this.mFile));
+ }
+ }
+ if (!f.isFile()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(
+ new ExecutionException("path exists but it's not a file")); //$NON-NLS-1$
+ }
+ }
+
+ // Read the file
+ read(f);
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(this.mCancelled);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Method that read the file
+ *
+ * @param file The file to read
+ */
+ private void read(File file) {
+ // Read the file
+ BufferedInputStream bis = null;
+ try {
+ bis = new BufferedInputStream(new FileInputStream(file), getBufferSize());
+ int read = 0;
+ byte[] data = new byte[getBufferSize()];
+ while ((read = bis.read(data, 0, getBufferSize())) != -1) {
+ if (this.mAsyncResultListener != null) {
+ byte[] readData = new byte[read];
+ System.arraycopy(data, 0, readData, 0, read);
+ this.mAsyncResultListener.onPartialResult(readData);
+
+ // Check if the process was cancelled
+ try {
+ synchronized (this.mSync) {
+ if (this.mCancelled || this.mEnded) {
+ this.mSync.notify();
+ break;
+ }
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+
+ } catch (Exception e) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onException(new InsufficientPermissionsException());
+ }
+
+ } finally {
+ try {
+ if (bis != null) {
+ bis.close();
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ try {
+ synchronized (this.mSync) {
+ this.mCancelled = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ try {
+ synchronized (this.mSync) {
+ this.mEnded = true;
+ this.mSync.wait(5000L);
+ }
+ } catch (Exception e) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/ResolveLinkCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/ResolveLinkCommand.java
new file mode 100644
index 000000000..ad32c335c
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/ResolveLinkCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.ListExecutable.LIST_MODE;
+import me.toolify.backbone.commands.ResolveLinkExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.model.FileSystemObject;
+
+import java.io.File;
+
+
+/**
+ * A class for retrieve the real file name of a symlink. This command
+ * can be used too for retrieve the absolute path of a file or directory
+ */
+public class ResolveLinkCommand extends Program implements ResolveLinkExecutable {
+
+ private static final String TAG = "ResolveLinkCommand"; //$NON-NLS-1$
+
+ private final String mSrc;
+ private FileSystemObject mFso;
+
+ /**
+ * Constructor of ResolveLinkCommand.
+ *
+ * @param src The file system object to read
+ */
+ public ResolveLinkCommand(String src) {
+ super();
+ this.mSrc = src;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileSystemObject getResult() {
+ return this.mFso;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Resolving link of %s", //$NON-NLS-1$
+ this.mSrc));
+ }
+
+ File f = new File(this.mSrc);
+ if (!f.exists()) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$
+ }
+ throw new InsufficientPermissionsException();
+ }
+ try {
+ String absPath = f.getCanonicalPath();
+ ListCommand cmd = new ListCommand(absPath, LIST_MODE.FILEINFO);
+ cmd.execute();
+ this.mFso = cmd.getSingleResult();
+ } catch (Exception e) {
+ if (isTrace()) {
+ Log.v(TAG, "Result: FAIL. ExecutionException"); //$NON-NLS-1$
+ }
+ throw new ExecutionException("can't resolve link"); //$NON-NLS-1$
+ }
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Link: %s", //$NON-NLS-1$
+ this.mFso));
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/java/WriteCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/java/WriteCommand.java
new file mode 100644
index 000000000..d46b3fe08
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/java/WriteCommand.java
@@ -0,0 +1,228 @@
+/*
+ * 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 me.toolify.backbone.commands.java;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.WriteExecutable;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A class for write data to disk.
+ *
+ * User MUST call the {@link #createOutputStream()} to get the output stream where
+ * write the data.
. When no more exist then user MUST call the onEnd method
+ * of the asynchronous listener.
+ */
+public class WriteCommand extends Program implements WriteExecutable {
+
+ private static final String TAG = "WriteCommand"; //$NON-NLS-1$
+
+ private final String mFile;
+ private BufferedOutputStream mBuffer;
+ private final AsyncResultListener mAsyncResultListener;
+
+ private boolean mCancelled;
+ private final Object mSync = new Object();
+
+ private static final long TIMEOUT = 1000L;
+
+ private final Object mWriteSync = new Object();
+ private boolean mReady;
+
+ /**
+ * Constructor of WriteCommand.
+ *
+ * @param file The file where to write the data
+ * @param asyncResultListener The partial result listener
+ */
+ public WriteCommand(
+ String file, AsyncResultListener asyncResultListener) {
+ super();
+ this.mFile = file;
+ this.mAsyncResultListener = asyncResultListener;
+ this.mCancelled = false;
+ this.mReady = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAsynchronous() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public OutputStream createOutputStream() throws IOException {
+ try {
+ // Wait until command is ready
+ synchronized (this.mWriteSync) {
+ if (!this.mReady) {
+ try {
+ this.mWriteSync.wait(TIMEOUT);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+ this.mBuffer = new BufferedOutputStream(
+ new FileOutputStream(
+ new File(this.mFile)), getBufferSize());
+ return this.mBuffer;
+ } catch (IOException ioEx) {
+ if (isTrace()) {
+ Log.e(TAG, "Result: FAILED. IOException", ioEx); //$NON-NLS-1$
+ }
+ throw ioEx;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException {
+ synchronized (this.mSync) {
+ this.mReady = true;
+ this.mSync.notify();
+ }
+
+ if (isTrace()) {
+ Log.v(TAG,
+ String.format("Writing file %s", this.mFile)); //$NON-NLS-1$
+
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncStart();
+ }
+
+ // Wait the finalization
+ try {
+ synchronized (this.mSync) {
+ this.mSync.wait();
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncEnd(this.mCancelled);
+ }
+ if (this.mAsyncResultListener != null) {
+ this.mAsyncResultListener.onAsyncExitCode(0);
+ }
+
+ if (isTrace()) {
+ Log.v(TAG, "Result: OK"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancelled() {
+ synchronized (this.mSync) {
+ return this.mCancelled;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean cancel() {
+ closeBuffer();
+ this.mCancelled = true;
+ try {
+ synchronized (this.mSync) {
+ this.mSync.notify();
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean end() {
+ closeBuffer();
+ try {
+ synchronized (this.mSync) {
+ this.mSync.notify();
+ }
+ } catch (Throwable _throw) {/**NON BLOCK**/}
+ return true;
+ }
+
+ /**
+ * Method that close the buffer
+ */
+ private void closeBuffer() {
+ try {
+ if (this.mBuffer != null) {
+ this.mBuffer.close();
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ try {
+ Thread.yield();
+ } catch (Exception ex) {/**NON BLOCK**/}
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnEndListener(OnEndListener onEndListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setOnCancelListener(OnCancelListener onCancelListener) {
+ //Ignore. Java console don't use this
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isCancellable() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public AsyncResultListener getAsyncResultListener() {
+ return this.mAsyncResultListener;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgram.java
similarity index 91%
rename from src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgram.java
index 64dc0e53f..ca3908abf 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgram.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgram.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.util.FileHelper;
+import me.toolify.backbone.commands.AsyncResultExecutable;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.util.FileHelper;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,7 +51,7 @@ public abstract class AsyncResultProgram
* @hide
*/
final List mPartialDataType;
- private final Object mSync = new Object();
+ final Object mSync = new Object();
/**
* @hide
*/
@@ -106,7 +106,7 @@ public AsyncResultProgram(
* @hide
*/
public final void onRequestStartParsePartialResult() {
- this.mWorkerThread = new AsyncResultProgramThread(this.mSync);
+ this.mWorkerThread = new AsyncResultProgramThread();
this.mWorkerThread.start();
//Notify start to command class
@@ -131,17 +131,12 @@ public final void onRequestEndParsePartialResult(boolean cancelled) {
this.mSync.notify();
}
synchronized (this.mTerminateSync) {
- try {
- this.mSync.wait();
- } catch (Exception e) {
- /**NON BLOCK**/
- }
- try {
- if (this.mWorkerThread.isAlive()) {
- this.mWorkerThread.interrupt();
+ if (this.mWorkerThread.isAlive()) {
+ try {
+ this.mTerminateSync.wait();
+ } catch (Exception e) {
+ /**NON BLOCK**/
}
- } catch (Exception e) {
- /**NON BLOCK**/
}
}
@@ -327,6 +322,15 @@ public final void setOnEndListener(OnEndListener onEndListener) {
this.mOnEndListener = onEndListener;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean isIndefinitelyWait() {
+ // Asynchronous programs should wait indefinitely for its nature
+ return true;
+ }
+
/**
* {@inheritDoc}
*/
@@ -353,16 +357,12 @@ public boolean isExpectEnd() {
*/
private class AsyncResultProgramThread extends Thread {
boolean mAlive = true;
- private final Object mSyncObj;
/**
* Constructor of AsyncResultProgramThread.
- *
- * @param sync The synchronized object
*/
- AsyncResultProgramThread(Object sync) {
+ AsyncResultProgramThread() {
super();
- this.mSyncObj = sync;
}
/**
@@ -373,12 +373,9 @@ public void run() {
try {
this.mAlive = true;
while (this.mAlive) {
- synchronized (this.mSyncObj) {
- this.mSyncObj.wait();
+ synchronized (AsyncResultProgram.this.mSync) {
+ AsyncResultProgram.this.mSync.wait();
while (AsyncResultProgram.this.mPartialData.size() > 0) {
- if (!this.mAlive) {
- return;
- }
Byte type = AsyncResultProgram.this.mPartialDataType.remove(0);
String data = AsyncResultProgram.this.mPartialData.remove(0);
try {
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgramListener.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgramListener.java
similarity index 94%
rename from src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgramListener.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgramListener.java
index 158023c30..fd32ca8ba 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/AsyncResultProgramListener.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/AsyncResultProgramListener.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
+import me.toolify.backbone.commands.SIGNAL;
/**
* An interface for communicate shell results in a asynchronous way.
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/BashShell.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/BashShell.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/shell/BashShell.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/BashShell.java
index a357447ad..b9f116e09 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/BashShell.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/BashShell.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
/**
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ChangeOwnerCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangeOwnerCommand.java
similarity index 82%
rename from src/com/cyanogenmod/filemanager/commands/shell/ChangeOwnerCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangeOwnerCommand.java
index 999ac5400..d5234b9a9 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ChangeOwnerCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangeOwnerCommand.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.util.MountPointHelper;
+import me.toolify.backbone.commands.ChangeOwnerExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.model.User;
+import me.toolify.backbone.util.MountPointHelper;
import java.text.ParseException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ChangePermissionsCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangePermissionsCommand.java
similarity index 83%
rename from src/com/cyanogenmod/filemanager/commands/shell/ChangePermissionsCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangePermissionsCommand.java
index 4c2bf60a4..70ef29e73 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ChangePermissionsCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChangePermissionsCommand.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.model.Permissions;
-import com.cyanogenmod.filemanager.util.MountPointHelper;
+import me.toolify.backbone.commands.ChangePermissionsExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.model.Permissions;
+import me.toolify.backbone.util.MountPointHelper;
import java.text.ParseException;
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChecksumCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChecksumCommand.java
new file mode 100644
index 000000000..fc4ad567a
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ChecksumCommand.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ChecksumExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+
+import java.io.File;
+
+/**
+ * A class for calculate MD5 and SHA-1 checksums of a file system object.
+ *
+ * Partial results are returned in order (MD5 -> SHA1)
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?md5sum"}
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?sha1sum"}
+ * @see me.toolify.backbone.commands.ChecksumExecutable.CHECKSUMS
+ */
+public class ChecksumCommand extends AsyncResultProgram implements ChecksumExecutable {
+
+ private static final String ID = "checksum"; //$NON-NLS-1$
+
+ private final String mName;
+ private final String[] mChecksums;
+ private int mChecksumsCounter;
+ private String mPartial;
+
+ /**
+ * Constructor of ChecksumCommand.
+ *
+ * @param src The source file
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ChecksumCommand(String src, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, src);
+ this.mChecksums = new String[]{null, null};
+ this.mName = new File(src).getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ this.mChecksums[0] = null;
+ this.mChecksums[1] = null;
+ this.mChecksumsCounter = 0;
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {
+ // Send the last partial data
+ if (this.mPartial != null && this.mPartial.length() > 0) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(this.mPartial);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+ if (partialIn == null || partialIn.length() ==0) return;
+ boolean endsWithNewLine = partialIn.endsWith("\n"); //$NON-NLS-1$
+ String[] lines = partialIn.split("\n"); //$NON-NLS-1$
+
+ // Append the pending data to the first line
+ lines[0] = this.mPartial + lines[0];
+
+ // Return all the lines, except the last
+ for (int i = 0; i < lines.length-1; i++) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[i]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+
+ // Return the last line?
+ if (endsWithNewLine) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[lines.length-1]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ } else {
+ // Save the partial for next calls
+ this.mPartial = lines[lines.length-1];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ try {
+ if (this.getProgramListener().getOutputStream() != null) {
+ this.getProgramListener().getOutputStream().flush();
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ try {
+ Thread.yield();
+ } catch (Exception ex) {/**NON BLOCK**/}
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getResult() {
+ return this.mChecksums;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChecksum(CHECKSUMS checksum) {
+ return getResult()[checksum.ordinal()];
+ }
+
+ /**
+ * Method that processes a line to determine if it's a valid partial result
+ *
+ * @param line The line to process
+ * @return String The processed line
+ */
+ private String processPartialResult(String line) {
+ // MD5 and SHA-1 return both the digest and the name of the file
+ // 4c044b884cf2ff3839713da0e81dced19f099b09 boot.zip
+ int pos = line.indexOf(" "); //$NON-NLS-1$
+ if (line.endsWith(this.mName) && pos != -1) {
+ String digest = line.substring(0, pos).trim();
+ if (this.mChecksumsCounter < this.mChecksums.length) {
+ this.mChecksums[this.mChecksumsCounter] = digest;
+ }
+ this.mChecksumsCounter++;
+ return digest;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ //Ignore exit code 143 (cancelled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/Command.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Command.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/shell/Command.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/Command.java
index cf9a9f5ce..b349ca425 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/Command.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Command.java
@@ -14,21 +14,19 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-
-import com.android.internal.util.XmlUtils;
-import com.cyanogenmod.filemanager.FileManagerApplication;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.util.ShellHelper;
-
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.util.ShellHelper;
+import me.toolify.backbone.util.XmlUtils;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/CompressCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CompressCommand.java
new file mode 100644
index 000000000..560fe6106
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CompressCommand.java
@@ -0,0 +1,373 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.CompressExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.preferences.CompressionMode;
+import me.toolify.backbone.util.FileHelper;
+
+import java.io.File;
+
+/**
+ * A class for compress file system objects
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?tar"}
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?gzip"}
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?bzip2"}
+ */
+public class CompressCommand extends AsyncResultProgram implements CompressExecutable {
+
+ /**
+ * An enumeration of implemented compression modes.
+ */
+ private enum Mode {
+ /**
+ * Archive using Tar algorithm
+ */
+ A_TAR(TAR_ID, "", CompressionMode.A_TAR), //$NON-NLS-1$
+ /**
+ * Archive and compress using Gzip algorithm
+ */
+ AC_GZIP(TAR_ID, "z", CompressionMode.AC_GZIP), //$NON-NLS-1$
+ /**
+ * Archive and compress using Gzip algorithm
+ */
+ AC_GZIP2(TAR_ID, "z", CompressionMode.AC_GZIP2), //$NON-NLS-1$
+ /**
+ * Archive and compress using Bzip algorithm
+ */
+ AC_BZIP(TAR_ID, "j", CompressionMode.AC_BZIP), //$NON-NLS-1$
+ /**
+ * Compress using Gzip algorithm
+ */
+ C_GZIP(GZIP_ID, "z", CompressionMode.C_GZIP), //$NON-NLS-1$
+ /**
+ * Compress using Bzip algorithm
+ */
+ C_BZIP(BZIP_ID, "j", CompressionMode.C_BZIP), //$NON-NLS-1$
+ /**
+ * Archive using Zip algorithm
+ */
+ A_ZIP(ZIP_ID, "", CompressionMode.A_ZIP); //$NON-NLS-1$
+
+ final String mId;
+ final String mFlag;
+ final CompressionMode mMode;
+
+ /**
+ * Constructor of Mode
+ *
+ * @param id The command identifier
+ * @param flag The tar compression flag
+ * @param mode The compression mode
+ */
+ private Mode(String id, String flag, CompressionMode mode) {
+ this.mId = id;
+ this.mFlag = flag;
+ this.mMode = mode;
+ }
+
+ /**
+ * Method that return the mode from his compression mode
+ *
+ * @param mode The compression mode
+ * @return Mode The mode
+ */
+ public static Mode fromCompressionMode(CompressionMode mode) {
+ Mode[] modes = Mode.values();
+ int cc = modes.length;
+ for (int i = 0; i < cc; i++) {
+ if (modes[i].mMode.compareTo(mode) == 0) {
+ return modes[i];
+ }
+ }
+ return null;
+ }
+ }
+
+ private static final String TAR_ID = "tar"; //$NON-NLS-1$
+ private static final String GZIP_ID = "gzip"; //$NON-NLS-1$
+ private static final String BZIP_ID = "bzip"; //$NON-NLS-1$
+ private static final String ZIP_ID = "zip"; //$NON-NLS-1$
+
+ private Boolean mResult;
+ private String mPartial;
+
+ private final Mode mMode;
+ private final String mOutFile;
+
+ /**
+ * Constructor of CompressCommand. This method creates an archive-compressed
+ * file from one or various file system objects.
+ *
+ * @param mode The compression mode
+ * @param dst The absolute path of the new compress file
+ * @param src An array of file system objects to compress
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public CompressCommand(
+ CompressionMode mode, String dst, String[] src, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(Mode.fromCompressionMode(mode).mId,
+ asyncResultListener,
+ resolveArchiveArgs(Mode.fromCompressionMode(mode), dst));
+ this.mMode = Mode.fromCompressionMode(mode);
+
+ if (!this.mMode.mMode.mArchive) {
+ throw new InvalidCommandDefinitionException(
+ "Unsupported archive mode"); //$NON-NLS-1$
+ }
+
+ //Convert the arguments from absolute to relative
+ addExpandedArguments(
+ convertAbsolutePathsToRelativePaths(dst, src), true);
+
+ // Create the output file
+ this.mOutFile = dst;
+ }
+
+ /**
+ * Constructor of CompressCommand. This method creates a compressed
+ * file from one file.
+ *
+ * @param mode The compression mode
+ * @param src The file to compress
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public CompressCommand(
+ CompressionMode mode, String src, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(Mode.fromCompressionMode(mode).mId,
+ asyncResultListener,
+ resolveCompressArgs(mode, src));
+ this.mMode = Mode.fromCompressionMode(mode);
+
+ if (this.mMode.mMode.mArchive) {
+ throw new InvalidCommandDefinitionException(
+ "Unsupported compression mode"); //$NON-NLS-1$
+ }
+
+ // Create the output file
+ this.mOutFile = resolveOutputFile(mode, src);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ this.mResult = Boolean.FALSE;
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {
+ // Send the last partial data
+ if (this.mPartial != null && this.mPartial.length() > 0) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(this.mPartial);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+ if (partialIn == null || partialIn.length() ==0) return;
+ boolean endsWithNewLine = partialIn.endsWith("\n"); //$NON-NLS-1$
+ String[] lines = partialIn.split("\n"); //$NON-NLS-1$
+
+ // Append the pending data to the first line
+ lines[0] = this.mPartial + lines[0];
+
+ // Return all the lines, except the last
+ int cc = lines.length;
+ for (int i = 0; i < cc-1; i++) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[i]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ }
+
+ // Return the last line?
+ if (endsWithNewLine) {
+ if (getAsyncResultListener() != null) {
+ String data = processPartialResult(lines[lines.length-1]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
+ }
+ this.mPartial = ""; //$NON-NLS-1$
+ } else {
+ // Save the partial for next calls
+ this.mPartial = lines[lines.length-1];
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mResult;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+
+ //Ignore exit code 143 (cancelled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 1 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+
+ // Correct
+ this.mResult = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getOutCompressedFile() {
+ return this.mOutFile;
+ }
+
+ /**
+ * Method that resolves the arguments for the archive mode
+ *
+ * @return String[] The arguments
+ */
+ private final static String[] resolveArchiveArgs(Mode mode, String dst) {
+ if (mode.compareTo(Mode.A_ZIP) == 0) {
+ return new String[]{
+ FileHelper.getParentDir(dst),
+ dst
+ };
+ }
+ return new String[]{
+ FileHelper.getParentDir(dst),
+ mode.mFlag,
+ dst
+ };
+ }
+
+ /**
+ * Method that resolves the arguments for the compression mode
+ *
+ * @return String[] The arguments
+ */
+ private static String[] resolveCompressArgs(CompressionMode mode, String src) {
+ switch (mode) {
+ case C_GZIP:
+ case C_BZIP:
+ return new String[]{src};
+ default:
+ return new String[]{};
+ }
+ }
+
+ /**
+ * Method that processes a line to determine if it's a valid partial result
+ *
+ * @param line The line to process
+ * @return String The processed line
+ */
+ private String processPartialResult(String line) {
+ if (this.mMode.compareTo(Mode.A_ZIP) == 0) {
+ if (line.startsWith(" adding: ")) { //$NON-NLS-1$
+ int pos = line.lastIndexOf('(');
+ if (pos != -1) {
+ // Remove progress
+ return line.substring(10, pos).trim();
+ }
+ return line.substring(10).trim();
+ }
+ return null;
+ }
+ return line;
+ }
+
+ /**
+ * Method that resolves the output path of the compressed file
+ *
+ * @return String The output path of the compressed file
+ */
+ private static String resolveOutputFile(CompressionMode mode, String src) {
+ return String.format("%s.%s", src, mode.mExtension); //$NON-NLS-1$
+ }
+
+ /**
+ * Method that converts the absolute paths of the source files to relative paths
+ *
+ * @param dst The destination compressed file
+ * @param src The source uncompressed files
+ * @return String[] The array of relative paths
+ */
+ private static String[] convertAbsolutePathsToRelativePaths(String dst, String[] src) {
+ File parent = new File(dst).getParentFile();
+ String p = File.separator;
+ if (parent != null) {
+ p = parent.getAbsolutePath();
+ }
+
+ // Converts every path
+ String[] out = new String[src.length];
+ int cc = src.length;
+ for (int i = 0; i < cc; i++) {
+ out[i] = FileHelper.toRelativePath(src[i], p);
+ }
+ return out;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/CopyCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CopyCommand.java
new file mode 100644
index 000000000..452a0103f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CopyCommand.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.CopyExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for copy a file or directory.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?cp"}
+ */
+public class CopyCommand extends SyncResultProgram implements CopyExecutable {
+
+ private static final String ID = "cp"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mDst;
+
+ /**
+ * Constructor of CopyCommand.
+ *
+ * @param src The name of the file or directory to be copied
+ * @param dst The name of the file or directory in which copy the source file or directory
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public CopyCommand(String src, String dst) throws InvalidCommandDefinitionException {
+ super(ID, src, dst);
+ this.mDst = dst;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mDst);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIndefinitelyWait() {
+ return true;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateDirCommand.java
new file mode 100644
index 000000000..4a48cbf99
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateDirCommand.java
@@ -0,0 +1,94 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.CreateDirExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for create a directory.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?mkdir"}
+ */
+public class CreateDirCommand extends SyncResultProgram implements CreateDirExecutable {
+
+ private static final String ID = "mkdir"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mFileName;
+
+ /**
+ * Constructor of CreateDirCommand.
+ *
+ * @param fileName The name of the new directory
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public CreateDirCommand(String fileName) throws InvalidCommandDefinitionException {
+ super(ID, fileName);
+ this.mFileName = fileName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mFileName);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateFileCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateFileCommand.java
new file mode 100644
index 000000000..62f607de9
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/CreateFileCommand.java
@@ -0,0 +1,95 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.CreateFileExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for create a file (regular file).
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?echo"}. This class use echo instead of touch
+ * because echo returns better exit codes (permission denied, read-only, ..)
+ */
+public class CreateFileCommand extends SyncResultProgram implements CreateFileExecutable {
+
+ private static final String ID = "touch"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mFileName;
+
+ /**
+ * Constructor of CreateFileCommand.
+ *
+ * @param fileName The name of the new file
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public CreateFileCommand(String fileName) throws InvalidCommandDefinitionException {
+ super(ID, fileName);
+ this.mFileName = fileName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mFileName);
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteDirCommand.java
new file mode 100644
index 000000000..3e11fbaaa
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteDirCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.DeleteDirExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for delete a file (regular file).
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?rm"}
+ */
+public class DeleteDirCommand extends SyncResultProgram implements DeleteDirExecutable {
+
+ private static final String ID = "rmdir"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mFileName;
+
+ /**
+ * Constructor of DeleteDirCommand.
+ *
+ * @param fileName The name of the directory to be deleted
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public DeleteDirCommand(String fileName) throws InvalidCommandDefinitionException {
+ super(ID, fileName);
+ this.mFileName = fileName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mFileName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIndefinitelyWait() {
+ return true;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteFileCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteFileCommand.java
new file mode 100644
index 000000000..9e7a26e6f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DeleteFileCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.DeleteFileExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for delete a file (regular file).
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?rm"}
+ */
+public class DeleteFileCommand extends SyncResultProgram implements DeleteFileExecutable {
+
+ private static final String ID = "rm"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mFileName;
+
+ /**
+ * Constructor of DeleteFileCommand.
+ *
+ * @param fileName The name of the file to be deleted
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public DeleteFileCommand(String fileName) throws InvalidCommandDefinitionException {
+ super(ID, fileName);
+ this.mFileName = fileName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mFileName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIndefinitelyWait() {
+ return true;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/DiskUsageCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DiskUsageCommand.java
new file mode 100644
index 000000000..81c557f1f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/DiskUsageCommand.java
@@ -0,0 +1,141 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.DiskUsageExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.DiskUsage;
+import me.toolify.backbone.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for get information about disk usage.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?df"}
+ */
+public class DiskUsageCommand extends SyncResultProgram implements DiskUsageExecutable {
+
+ private static final String ID = "diskusage"; //$NON-NLS-1$
+ private static final String ID_ALL = "diskusageall"; //$NON-NLS-1$
+
+ private final List mDisksUsage;
+
+ /**
+ * Constructor of DiskUsageCommand.
+ *
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public DiskUsageCommand() throws InvalidCommandDefinitionException {
+ super(ID_ALL, new String[]{});
+ this.mDisksUsage = new ArrayList();
+ }
+
+ /**
+ * Constructor of DiskUsageCommand.
+ *
+ * @param dir The directory of which obtain its disk usage
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public DiskUsageCommand(String dir) throws InvalidCommandDefinitionException {
+ super(ID, new String[]{dir});
+ this.mDisksUsage = new ArrayList();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the array
+ this.mDisksUsage.clear();
+
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ int line = 0;
+ try {
+ br = new BufferedReader(new StringReader(in));
+ String szLine = br.readLine(); //The first line must be ignored
+ while ((szLine = br.readLine()) != null) {
+ //Checks that there is some text in the line. Otherwise ignore it
+ if (szLine.trim().length() == 0) {
+ break;
+ }
+
+ //Parse the line into a DiskUsage reference
+ try {
+ this.mDisksUsage.add(ParseHelper.toDiskUsage(szLine));
+ } catch (ParseException pEx) {
+ //Ignore
+ }
+
+ line++;
+ }
+
+ } catch (IOException ioEx) {
+ throw new ParseException(ioEx.getMessage(), line);
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), line);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mDisksUsage;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0 && exitCode != 1) { //Permission denied
+ throw new ExecutionException("exitcode != 0 && != 1"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/EchoCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/EchoCommand.java
similarity index 89%
rename from src/com/cyanogenmod/filemanager/commands/shell/EchoCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/EchoCommand.java
index 8488343f4..86fad6bb0 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/EchoCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/EchoCommand.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.EchoExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import me.toolify.backbone.commands.EchoExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ExecCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ExecCommand.java
similarity index 87%
rename from src/com/cyanogenmod/filemanager/commands/shell/ExecCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/ExecCommand.java
index c66cef9f9..ff59732ed 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ExecCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ExecCommand.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.commands.ExecExecutable;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ExecExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
/**
* A class for execute a command
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/FindCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/FindCommand.java
new file mode 100644
index 000000000..ec380836f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/FindCommand.java
@@ -0,0 +1,199 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.FindExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.ParseHelper;
+import me.toolify.backbone.util.SearchHelper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for search files.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?find"}
+ */
+public class FindCommand extends AsyncResultProgram implements FindExecutable {
+
+ private static final String TAG = "FindCommand"; //$NON-NLS-1$
+
+ private static final String ID = "find"; //$NON-NLS-1$
+
+ private final File mDirectory;
+
+ /**
+ * Constructor of FindCommand.
+ *
+ * @param directory The absolute path of the directory where do the search
+ * @param query The terms to be searched
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public FindCommand(
+ String directory, Query query, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, createArgs(FileHelper.addTrailingSlash(directory), query));
+ this.mDirectory = new File(directory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {
+ //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+
+ // Check the in buffer to extract information
+ final List partialFiles = new ArrayList();
+ BufferedReader br = null;
+ try {
+ //Read the partial + previous partial and clean partial
+ br = new BufferedReader(new StringReader(partialIn));
+
+ //Add all lines to an array
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ //Checks that there is some text in the line. Otherwise ignore it
+ if (line.trim().length() == 0) {
+ break;
+ }
+
+ // Add to the list
+ try {
+ FileSystemObject fso = ParseHelper.parseStatOutput(line);
+
+ // Search directory is not part of the search
+ if (fso.getFullPath().compareTo(this.mDirectory.getAbsolutePath()) != 0) {
+ partialFiles.add(fso);
+ }
+
+ } catch (Exception e) {
+ // Log the parsing error
+ if (isTrace()) {
+ Log.w(TAG,
+ String.format(
+ "Failed to parse output: %s", //$NON-NLS-1$
+ String.valueOf(line)));
+ }
+ }
+ }
+
+ //If a listener is defined, then send the partial result
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(partialFiles);
+ }
+
+ } 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 void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+
+ //Search in a subdirectory without permissions returns 1, but this
+ //not must be treated as an error
+ //Ignore exit code 143 (cancelled)
+ //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$
+ }
+ }
+
+ /**
+ * Method that create the arguments of this command, using the directory and
+ * arguments and creating the regular expressions of the search.
+ *
+ * @param directory The directory where to search
+ * @param query The query make for user
+ * @return String[] The arguments of the command
+ */
+ private static String[] createArgs(String directory, Query query) {
+ String[] args = new String[query.getSlotsCount() + 1];
+ args[0] = directory;
+ int cc = query.getSlotsCount();
+ for (int i = 0; i < cc; i++) {
+ args[i + 1] = SearchHelper.toIgnoreCaseRegExp(query.getSlot(i), false);
+ }
+ return args;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/FolderUsageCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/FolderUsageCommand.java
new file mode 100644
index 000000000..5151f6347
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/FolderUsageCommand.java
@@ -0,0 +1,265 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.FolderUsageExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.BlockDevice;
+import me.toolify.backbone.model.CharacterDevice;
+import me.toolify.backbone.model.Directory;
+import me.toolify.backbone.model.DomainSocket;
+import me.toolify.backbone.model.FolderUsage;
+import me.toolify.backbone.model.NamedPipe;
+import me.toolify.backbone.model.Symlink;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.MimeTypeHelper;
+import me.toolify.backbone.util.MimeTypeHelper.MimeTypeCategory;
+
+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 = "folderusage"; //$NON-NLS-1$
+
+ private final String mDirectory;
+ private FolderUsage mFolderUsage;
+
+ /**
+ * Constructor of FolderUsageCommand.
+ *
+ * @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, asyncResultListener, new String[]{directory});
+ this.mFolderUsage = new FolderUsage(directory);
+ this.mDirectory = directory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ this.mFolderUsage = new FolderUsage(this.mDirectory);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {
+ //$NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ try {
+ // Parse the line. We expect a ls -l output line
+ // -rw-r--r-- root root 7 2012-12-30 00:49 test.txt
+ //
+ // (1) permissions
+ // (2) number of links and directories
+ // (3) owner
+ // (4) group
+ // (5) size
+ // (6) date
+ // (7) name
+
+ //Partial contains full lines
+ br = new BufferedReader(new StringReader(partialIn));
+
+ //Add all lines to an array
+ List lines = new ArrayList();
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ // Discard empty, paths, and folder links
+ if (line.length() == 0 ||
+ line.startsWith(FileHelper.ROOT_DIRECTORY) ||
+ line.startsWith(FileHelper.CURRENT_DIRECTORY) ||
+ line.startsWith(FileHelper.PARENT_DIRECTORY)) {
+ continue;
+ }
+ lines.add(line);
+ }
+
+ int c = 0;
+ try {
+ while (lines.size() > 0) {
+ // Retrieve the info
+ String szLine = lines.remove(0).trim();
+ try {
+ // Clean the line (we don't care about names, only need the extension)
+ // so remove spaces is safe here
+ while (szLine.indexOf(" ") != -1) { //$NON-NLS-1$
+ szLine = szLine.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ // Don't compute . and ..
+ // This is not secure, but we don't need a exact precission on this
+ // method
+ if (szLine.length() == 0 ||
+ szLine.endsWith(" " + FileHelper.CURRENT_DIRECTORY) || //$NON-NLS-1$
+ szLine.endsWith(" " + FileHelper.PARENT_DIRECTORY)) { //$NON-NLS-1$
+ c++;
+ continue;
+ }
+
+ char type = szLine.charAt(0);
+ if (type == Symlink.UNIX_ID ||
+ type == BlockDevice.UNIX_ID ||
+ type == CharacterDevice.UNIX_ID ||
+ type == DomainSocket.UNIX_ID ||
+ type == NamedPipe.UNIX_ID) {
+ // File + Category
+ this.mFolderUsage.addFile();
+ if (type == Symlink.UNIX_ID) {
+ this.mFolderUsage.addFileToCategory(MimeTypeCategory.NONE);
+ } else {
+ this.mFolderUsage.addFileToCategory(MimeTypeCategory.SYSTEM);
+ }
+
+ } else if (type == Directory.UNIX_ID) {
+ // Folder
+ this.mFolderUsage.addFolder();
+
+ } else {
+ // File + Category + Size
+ try {
+ // we need a valid line
+ String[] fields = szLine.split(" "); //$NON-NLS-1$
+ if (fields.length < 8) {
+ continue;
+ }
+
+ long size = Long.parseLong(fields[4]);
+ String name = fields[fields.length-1];// We only need the extension
+ String ext = FileHelper.getExtension(name);
+ MimeTypeCategory category =
+ MimeTypeHelper.getCategoryFromExt(null, ext);
+ this.mFolderUsage.addFile();
+ this.mFolderUsage.addFileToCategory(category);
+ this.mFolderUsage.addSize(size);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ c++;
+
+ } catch (Exception e) {
+ // Ignore.
+ }
+
+ // Partial notification
+ if (c % 5 == 0) {
+ //If a listener is defined, then send the partial result
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(this.mFolderUsage);
+ }
+ }
+ }
+ } catch (Exception ex) { /**NON BLOCK **/ }
+
+ //If a listener is defined, then send the partial result
+ if (getAsyncResultListener() != null) {
+ 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 void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FolderUsage getFolderUsage() {
+ return this.mFolderUsage;
+ }
+
+ /**
+ * {@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 (cancelled)
+ //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/filemanager/commands/shell/GroupsCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/GroupsCommand.java
similarity index 87%
rename from src/com/cyanogenmod/filemanager/commands/shell/GroupsCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/GroupsCommand.java
index e6eb99d0f..494d59df3 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/GroupsCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/GroupsCommand.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.GroupsExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.util.FileHelper;
+import me.toolify.backbone.commands.GroupsExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.util.FileHelper;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/IdentityCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/IdentityCommand.java
similarity index 91%
rename from src/com/cyanogenmod/filemanager/commands/shell/IdentityCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/IdentityCommand.java
index e87a90eca..10d73115a 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/IdentityCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/IdentityCommand.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
-
-import com.cyanogenmod.filemanager.commands.IdentityExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.AID;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.Identity;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.util.FileHelper;
+package me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.IdentityExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.AID;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.model.Identity;
+import me.toolify.backbone.model.User;
+import me.toolify.backbone.util.FileHelper;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/InvalidCommandDefinitionException.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/InvalidCommandDefinitionException.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/commands/shell/InvalidCommandDefinitionException.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/InvalidCommandDefinitionException.java
index 93ea24981..8bd53f447 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/InvalidCommandDefinitionException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/InvalidCommandDefinitionException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
/**
* An exception thrown when the information of the command is
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/LinkCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/LinkCommand.java
similarity index 84%
rename from src/com/cyanogenmod/filemanager/commands/shell/LinkCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/LinkCommand.java
index 9dc0ac7ca..34dcb537d 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/LinkCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/LinkCommand.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.LinkExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.util.MountPointHelper;
+import me.toolify.backbone.commands.LinkExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
import java.text.ParseException;
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ListCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ListCommand.java
new file mode 100644
index 000000000..7f89e5a67
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ListCommand.java
@@ -0,0 +1,215 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import android.util.Log;
+
+import me.toolify.backbone.commands.ListExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.shell.ShellConsole;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.model.ParentDirectory;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for list information about files and directories.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?stat"}
+ */
+public class ListCommand extends SyncResultProgram implements ListExecutable {
+
+ private static final String TAG = "ListCommand"; //$NON-NLS-1$
+
+ private static final String ID_LS = "ls"; //$NON-NLS-1$
+ private static final String ID_FILEINFO = "fileinfo"; //$NON-NLS-1$
+
+ private final LIST_MODE mMode;
+ private final List mFiles;
+ private String mParentDir;
+
+ /**
+ * Constructor of ListCommand. List mode.
+ *
+ * @param src The file system object to be listed
+ * @param console The console in which retrieve the parent directory information.
+ * null to attach to the default console
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ListCommand(String src, ShellConsole console)
+ throws InvalidCommandDefinitionException {
+ // Always add backslash for list the files of the directory, instead of
+ // the directory.
+ super(ID_LS, new String[]{ FileHelper.addTrailingSlash(src) });
+
+ //Initialize files to something distinct of null
+ this.mFiles = new ArrayList();
+ this.mMode = LIST_MODE.DIRECTORY;
+
+ //Retrieve parent directory information
+ if (src.compareTo(FileHelper.ROOT_DIRECTORY) == 0) {
+ this.mParentDir = null;
+ } else {
+ this.mParentDir = new File(src).getAbsolutePath();
+ }
+ }
+
+ /**
+ * Constructor of ListCommand. FileInfo mode
+ *
+ * @param src The file system object to be listed
+ * @param followSymlinks If follow the symlink
+ * @param console The console in which retrieve the parent directory information.
+ * null to attach to the default console
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ * @throws FileNotFoundException If the initial directory not exists
+ * @throws IOException If initial directory couldn't be checked
+ */
+ public ListCommand(String src, boolean followSymlinks, ShellConsole console)
+ throws InvalidCommandDefinitionException, FileNotFoundException, IOException {
+ // Always remove backslash for avoid listing the files of the directory, instead of
+ // the directory.
+ super(ID_FILEINFO,
+ new String[]{
+ FileHelper.removeTrailingSlash(
+ followSymlinks ?
+ new File(src).getCanonicalPath() :
+ new File(src).getAbsolutePath())});
+
+ //Initialize files to something distinct of null
+ this.mFiles = new ArrayList();
+ this.mMode = LIST_MODE.FILEINFO;
+
+ //Get the absolute path
+ if (followSymlinks) {
+ this.mParentDir =
+ FileHelper.removeTrailingSlash(
+ new File(src).getCanonicalFile().getParent());
+ } else {
+ this.mParentDir =
+ FileHelper.removeTrailingSlash(
+ new File(src).getAbsoluteFile().getParent());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the array
+ this.mFiles.clear();
+
+ // Read every line and parse it
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new StringReader(in));
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ //Checks that there is some text in the line. Otherwise ignore it
+ if (line.trim().length() == 0) {
+ break;
+ }
+
+ // Parse and add to result files
+ try {
+ this.mFiles.add(ParseHelper.parseStatOutput(line));
+ } catch (Exception e) {
+ // Log the parsing error
+ if (isTrace()) {
+ Log.w(TAG,
+ String.format(
+ "Failed to parse output: %s", //$NON-NLS-1$
+ String.valueOf(line)));
+ }
+ }
+ }
+
+ // Add the parent directory
+ if (this.mParentDir != null &&
+ this.mParentDir.compareTo(FileHelper.ROOT_DIRECTORY) != 0 &&
+ this.mMode.compareTo(LIST_MODE.DIRECTORY) == 0) {
+ this.mFiles.add(0, new ParentDirectory(new File(this.mParentDir).getParent()));
+ }
+
+ } catch (IOException ioEx) {
+ throw new ParseException(ioEx.getMessage(), 0);
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), 0);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mFiles;
+ }
+
+ /**
+ * Method that returns a single result of the program invocation.
+ * Only must be called within a FILEINFO mode listing.
+ *
+ * @return FileSystemObject The file system object reference
+ */
+ public FileSystemObject getSingleResult() {
+ return this.mFiles.get(0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ // 123: stat failed ... Function not implemented (for broken symlinks)
+ if (exitCode != 0 && exitCode != 1 && exitCode != 123) {
+ throw new ExecutionException("exitcode != 0 && != 1 && != 123"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isWaitOnNewDataReceipt() {
+ return true;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/MountCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MountCommand.java
similarity index 84%
rename from src/com/cyanogenmod/filemanager/commands/shell/MountCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/MountCommand.java
index eae5e8310..62e30d33d 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/MountCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MountCommand.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.MountExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.model.MountPoint;
+import me.toolify.backbone.commands.MountExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
import java.text.ParseException;
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/MountPointInfoCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MountPointInfoCommand.java
new file mode 100644
index 000000000..6c10a11ed
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MountPointInfoCommand.java
@@ -0,0 +1,131 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.MountPointInfoExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for get information about disk usage.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?cat"}
+ * {@link
+ * "http://linux.web.cern.ch/linux/scientific5/docs/rhel/Deployment_Guide/s2-proc-mounts.html"}
+ */
+public class MountPointInfoCommand extends SyncResultProgram implements MountPointInfoExecutable {
+
+ private static final String ID = "mountpointinfo"; //$NON-NLS-1$
+
+ private final List mMountPoints;
+
+ /**
+ * Constructor of MountPointInfoCommand.
+ *
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public MountPointInfoCommand() throws InvalidCommandDefinitionException {
+ super(ID, new String[]{});
+ this.mMountPoints = new ArrayList();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the array
+ this.mMountPoints.clear();
+
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ int line = 0;
+ try {
+ br = new BufferedReader(new StringReader(in));
+ String szLine = null;
+ while ((szLine = br.readLine()) != null) {
+ //Checks that there is some text in the line. Otherwise ignore it
+ if (szLine.trim().length() == 0) {
+ break;
+ }
+
+ //Parse the line into a MountPoint reference
+ try {
+ this.mMountPoints.add(ParseHelper.toMountPoint(szLine));
+ } catch (ParseException pEx) {
+ //Ignore
+ }
+
+ line++;
+ }
+
+ } catch (IOException ioEx) {
+ throw new ParseException(ioEx.getMessage(), line);
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), line);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mMountPoints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0 && exitCode != 1) { //Permission denied
+ throw new ExecutionException("exitcode != 0 && != 1"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/MoveCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MoveCommand.java
new file mode 100644
index 000000000..a132ca0af
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/MoveCommand.java
@@ -0,0 +1,105 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.MoveExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.util.MountPointHelper;
+
+import java.text.ParseException;
+
+
+/**
+ * A class for move a file or directory.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?mv"}
+ */
+public class MoveCommand extends SyncResultProgram implements MoveExecutable {
+
+ private static final String ID = "mv"; //$NON-NLS-1$
+ private Boolean mRet;
+ private final String mSrc;
+ private final String mDst;
+
+ /**
+ * Constructor of MoveCommand.
+ *
+ * @param src The name of the file or directory to be moved
+ * @param dst The name of the file or directory in which move the source file or directory
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public MoveCommand(String src, String dst) throws InvalidCommandDefinitionException {
+ super(ID, src, dst);
+ this.mSrc = src;
+ this.mDst = dst;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mRet = Boolean.TRUE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Boolean getResult() {
+ return this.mRet;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getSrcWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mSrc);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MountPoint getDstWritableMountPoint() {
+ return MountPointHelper.getMountPointFromDirectory(this.mDst);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIndefinitelyWait() {
+ return true;
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ParentDirCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ParentDirCommand.java
new file mode 100644
index 000000000..6775e055f
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ParentDirCommand.java
@@ -0,0 +1,102 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.ParentDirExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.ParseException;
+
+
+/**
+ * A class for retrieve the parent directory of a file system object.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?dirname"}
+ */
+public class ParentDirCommand extends SyncResultProgram implements ParentDirExecutable {
+
+ private static final String ID = "dirname"; //$NON-NLS-1$
+ private String mParentDir;
+
+ /**
+ * Constructor of ParentDirCommand.
+ *
+ * @param src The file system object to read
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ParentDirCommand(String src) throws InvalidCommandDefinitionException {
+ super(ID, src);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mParentDir = ""; //$NON-NLS-1$
+
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new StringReader(in));
+ String szLine = br.readLine();
+ if (szLine != null) {
+ this.mParentDir = szLine;
+ }
+
+ } catch (IOException ioEx) {
+ throw new ParseException(ioEx.getMessage(), 0);
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), 0);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getResult() {
+ return this.mParentDir;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ if (exitCode != 0) {
+ throw new ExecutionException("exitcode != 0"); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ProcessIdCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ProcessIdCommand.java
new file mode 100644
index 000000000..7b795ee36
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ProcessIdCommand.java
@@ -0,0 +1,139 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.ProcessIdExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A class for retrieve the process identifier of a program.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?ps"}
+ */
+public class ProcessIdCommand extends SyncResultProgram implements ProcessIdExecutable {
+
+ private static final String ID_SHELL = "pid_shell"; //$NON-NLS-1$
+ private static final String ID_SHELL_CMDS = "pid_shell_cmds"; //$NON-NLS-1$
+ private static final String ID_CMD = "pid_cmd"; //$NON-NLS-1$
+ private List mPIDs;
+
+ /**
+ * Constructor of ProcessIdCommand.
+ * 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 ProcessIdCommand.
+ * Use this to retrieve all PIDs running on a shell.
+ *
+ * @param pid The process identifier of the shell when the process is running
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ProcessIdCommand(int pid) throws InvalidCommandDefinitionException {
+ super(ID_SHELL_CMDS, new String[]{String.valueOf(pid)});
+ }
+
+ /**
+ * Constructor of ProcessIdCommand.
+ * 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(int pid, String processName) throws InvalidCommandDefinitionException {
+ super(ID_CMD, new String[]{processName, String.valueOf(pid)});
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ //Release the return object
+ this.mPIDs = new ArrayList();
+
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new StringReader(in));
+ String szLine = br.readLine();
+ if (szLine == null) {
+ throw new ParseException("no information", 0); //$NON-NLS-1$
+ }
+ do {
+ // Add every PID
+ this.mPIDs.add(Integer.valueOf(szLine.trim()));
+
+ // Next line
+ szLine = br.readLine();
+ } while (szLine != null);
+
+ } catch (IOException ioEx) {
+ throw new ParseException(ioEx.getMessage(), 0);
+
+ } catch (ParseException pEx) {
+ throw pEx;
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), 0);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List getResult() {
+ return this.mPIDs;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException,
+ ExecutionException {
+ /**NON BLOCK**/
+ }
+
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/Program.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Program.java
new file mode 100644
index 000000000..094ee8ebc
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Program.java
@@ -0,0 +1,187 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.Executable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.console.OperationTimeoutException;
+
+import java.io.OutputStream;
+
+
+/**
+ * An abstract class that represents a command that need a shell
+ * to be executed.
+ */
+public abstract class Program extends Command implements Executable {
+
+ /**
+ * An interface for transmitting data to the program
+ */
+ public interface ProgramListener {
+ /**
+ * Invoked when a request the program need to write in the shell program.
+ *
+ * @param data The data to write to the shell console
+ * @param offset The initial position in buffer to store the bytes read from this stream
+ * @param byteCount The maximum number of bytes to store in b
+ * @return boolean If the write was transmitted successfully
+ * @throws ExecutionException If the console is not ready
+ */
+ boolean onRequestWrite(byte[] data, int offset, int byteCount) throws ExecutionException;
+
+ /**
+ * Method that returns the output stream of the console.
+ *
+ * @return OutputStream The output stream of the console
+ */
+ OutputStream getOutputStream();
+ }
+
+ // The listener for the program
+ private ProgramListener mProgramListener;
+
+ // Indicate if the program expect some output to stderr. If something is received
+ // in the stderr the program should be killed
+ private boolean mExitOnStdErrOutput;
+
+ /**
+ * @Constructor of Program
+ *
+ * @param id The resource identifier of the command
+ * @param args Arguments of the command (will be formatted with the arguments from
+ * the command definition)
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public Program(String id, String... args) throws InvalidCommandDefinitionException {
+ super(id, args);
+ this.mExitOnStdErrOutput = false;
+ }
+
+ /**
+ * @Constructor of Program
+ *
+ * @param id The resource identifier of the command
+ * @param prepare Indicates if the argument must be prepared
+ * @param args Arguments of the command (will be formatted with the arguments from
+ * the command definition)
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public Program(String id, boolean prepare, String... args)
+ throws InvalidCommandDefinitionException {
+ super(id, prepare, args);
+ this.mExitOnStdErrOutput = false;
+ }
+
+ /**
+ * Method that returns the program listener
+ *
+ * @return ProgramListener The program listener
+ */
+ protected ProgramListener getProgramListener() {
+ return this.mProgramListener;
+ }
+
+ /**
+ * Method that sets the program listener
+ *
+ * @param programListener The program listener
+ */
+ public void setProgramListener(ProgramListener programListener) {
+ this.mProgramListener = programListener;
+ }
+
+ /**
+ * Method that returns if the program should be killed if some output is received in
+ * the standard error buffer.
+ *
+ * @return boolean If the program should be killed
+ */
+ public boolean isExitOnStdErrOutput() {
+ return this.mExitOnStdErrOutput;
+ }
+
+ /**
+ * Method that sets if the program should be killed if some output is received in
+ * the standard error buffer.
+ *
+ * @param exitOnStdErrOutput If the program should be killed
+ */
+ public void setExitOnStdErrOutput(boolean exitOnStdErrOutput) {
+ this.mExitOnStdErrOutput = exitOnStdErrOutput;
+ }
+
+ /**
+ * Returns whether the shell should wait indefinitely for the end of the command.
+ *
+ * @return boolean If shell should wait indefinitely for the end of the command
+ * @hide
+ */
+ @SuppressWarnings("static-method")
+ public boolean isIndefinitelyWait() {
+ return false;
+ }
+
+ /**
+ * Returns whether the shell shouldn't raise a {@link OperationTimeoutException} when
+ * the program didn't exited but new data was received.
+ *
+ * @return boolean If shell shouldn't raise a {@link OperationTimeoutException} if new
+ * data was received
+ * @hide
+ */
+ @SuppressWarnings("static-method")
+ public boolean isWaitOnNewDataReceipt() {
+ return false;
+ }
+
+ /**
+ * Method that returns if the standard error must be
+ * ignored safely by the shell, and don't check for errors
+ * like NoSuchFileOrDirectory or
+ * Permission denied by the shell.
+ *
+ * @return boolean If the standard error must be ignored
+ * @hide
+ */
+ @SuppressWarnings("static-method")
+ public boolean isIgnoreShellStdErrCheck() {
+ return false;
+ }
+
+ /**
+ * Method that checks if the standard errors has exceptions.
+ *
+ * @param exitCode Program exit code
+ * @param err Standard Error buffer
+ * @throws InsufficientPermissionsException If an operation requires elevated permissions
+ * @throws NoSuchFileOrDirectory If the file or directory was not found
+ * @throws CommandNotFoundException If the command was not found
+ * @throws ExecutionException If the another exception is detected in the standard error
+ * @hide
+ */
+ @SuppressWarnings("unused")
+ public void checkStdErr(int exitCode, String err)
+ throws InsufficientPermissionsException, NoSuchFileOrDirectory,
+ CommandNotFoundException, ExecutionException {
+ /**NON BLOCK**/
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/QuickFolderSearchCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/QuickFolderSearchCommand.java
similarity index 89%
rename from src/com/cyanogenmod/filemanager/commands/shell/QuickFolderSearchCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/QuickFolderSearchCommand.java
index b060381be..b54f9efd8 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/QuickFolderSearchCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/QuickFolderSearchCommand.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.QuickFolderSearchExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import me.toolify.backbone.commands.QuickFolderSearchExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
import java.io.BufferedReader;
import java.io.File;
@@ -126,4 +126,12 @@ public boolean isIgnoreShellStdErrCheck() {
return true;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isWaitOnNewDataReceipt() {
+ return true;
+ }
+
}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ReadCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ReadCommand.java
new file mode 100644
index 000000000..d46d92cd7
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ReadCommand.java
@@ -0,0 +1,121 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ReadExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+
+/**
+ * A class for read a file
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?cat"}
+ */
+public class ReadCommand extends AsyncResultProgram implements ReadExecutable {
+
+ private static final String ID = "read"; //$NON-NLS-1$
+
+ /**
+ * Constructor of ExecCommand.
+ *
+ * @param file The file to read
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ReadCommand(
+ String file, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, new String[]{file});
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {
+ //If a listener is defined, then send the partial result
+ if (partialIn != null && partialIn.length() > 0) {
+ if (getAsyncResultListener() != null) {
+ getAsyncResultListener().onPartialResult(partialIn.getBytes());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean parseOnlyCompleteLines() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ // We have not privileges to read the file
+ if (exitCode == 1) {
+ throw new InsufficientPermissionsException();
+ }
+
+ //Ignore exit code 143 (cancelled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && && exitCode != 1 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+}
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/ResolveLinkCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ResolveLinkCommand.java
new file mode 100644
index 000000000..376b0d54b
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ResolveLinkCommand.java
@@ -0,0 +1,101 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.ResolveLinkExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.model.FileSystemObject;
+import me.toolify.backbone.util.FileHelper;
+import me.toolify.backbone.util.ParseHelper;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
+import java.text.ParseException;
+
+
+/**
+ * A class for retrieve the real file name of a symlink. This command
+ * can be used too for retrieve the absolute path of a file or directory
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?readlink"}
+ */
+public class ResolveLinkCommand extends SyncResultProgram implements ResolveLinkExecutable {
+
+ private static final String ID = "readlink"; //$NON-NLS-1$
+ private FileSystemObject mFso;
+
+ /**
+ * Constructor of ResolveLinkCommand.
+ *
+ * @param src The file system object to read
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public ResolveLinkCommand(String src) throws InvalidCommandDefinitionException {
+ super(ID, src,
+ (src.compareTo(FileHelper.ROOT_DIRECTORY) == 0) ?
+ FileHelper.ROOT_DIRECTORY :
+ new File(src).getParentFile().getAbsolutePath());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parse(String in, String err) throws ParseException {
+ // Check the in buffer to extract information
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new StringReader(in));
+
+ // Extract and parse the stat output
+ String line = br.readLine();
+ this.mFso = ParseHelper.parseStatOutput(line);
+
+ } catch (Exception ex) {
+ throw new ParseException(ex.getMessage(), 0);
+
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (Throwable ex) {
+ /**NON BLOCK**/
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileSystemObject getResult() {
+ return this.mFso;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ /**NON BLOCK**/
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/SendSignalCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SendSignalCommand.java
similarity index 87%
rename from src/com/cyanogenmod/filemanager/commands/shell/SendSignalCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/SendSignalCommand.java
index a8e9aac77..f626eeaf2 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/SendSignalCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SendSignalCommand.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.commands.SendSignalExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.commands.SendSignalExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
import java.text.ParseException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/Shell.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Shell.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/commands/shell/Shell.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/Shell.java
index 36b89fddc..9a65162ca 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/Shell.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/Shell.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
import android.util.Log;
-import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
-import com.cyanogenmod.filemanager.commands.WritableExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
-import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
+import me.toolify.backbone.commands.SyncResultExecutable;
+import me.toolify.backbone.commands.WritableExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.console.ReadOnlyFilesystemException;
/**
* An abstract class that represents a command to wrap others commands,
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableCreator.java
similarity index 83%
rename from src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableCreator.java
index adc2ea68b..2b4232b88 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableCreator.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableCreator.java
@@ -14,49 +14,50 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
-
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.commands.ChangeCurrentDirExecutable;
-import com.cyanogenmod.filemanager.commands.ChangeOwnerExecutable;
-import com.cyanogenmod.filemanager.commands.ChangePermissionsExecutable;
-import com.cyanogenmod.filemanager.commands.CompressExecutable;
-import com.cyanogenmod.filemanager.commands.CopyExecutable;
-import com.cyanogenmod.filemanager.commands.CreateDirExecutable;
-import com.cyanogenmod.filemanager.commands.CreateFileExecutable;
-import com.cyanogenmod.filemanager.commands.CurrentDirExecutable;
-import com.cyanogenmod.filemanager.commands.DeleteDirExecutable;
-import com.cyanogenmod.filemanager.commands.DeleteFileExecutable;
-import com.cyanogenmod.filemanager.commands.DiskUsageExecutable;
-import com.cyanogenmod.filemanager.commands.EchoExecutable;
-import com.cyanogenmod.filemanager.commands.ExecExecutable;
-import com.cyanogenmod.filemanager.commands.ExecutableCreator;
-import com.cyanogenmod.filemanager.commands.FindExecutable;
-import com.cyanogenmod.filemanager.commands.FolderUsageExecutable;
-import com.cyanogenmod.filemanager.commands.GroupsExecutable;
-import com.cyanogenmod.filemanager.commands.IdentityExecutable;
-import com.cyanogenmod.filemanager.commands.LinkExecutable;
-import com.cyanogenmod.filemanager.commands.ListExecutable;
-import com.cyanogenmod.filemanager.commands.MountExecutable;
-import com.cyanogenmod.filemanager.commands.MountPointInfoExecutable;
-import com.cyanogenmod.filemanager.commands.MoveExecutable;
-import com.cyanogenmod.filemanager.commands.ParentDirExecutable;
-import com.cyanogenmod.filemanager.commands.ProcessIdExecutable;
-import com.cyanogenmod.filemanager.commands.QuickFolderSearchExecutable;
-import com.cyanogenmod.filemanager.commands.ReadExecutable;
-import com.cyanogenmod.filemanager.commands.ResolveLinkExecutable;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.commands.SendSignalExecutable;
-import com.cyanogenmod.filemanager.commands.UncompressExecutable;
-import com.cyanogenmod.filemanager.commands.WriteExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.shell.ShellConsole;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.MountPoint;
-import com.cyanogenmod.filemanager.model.Permissions;
-import com.cyanogenmod.filemanager.model.Query;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.preferences.CompressionMode;
+package me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.ChangeOwnerExecutable;
+import me.toolify.backbone.commands.ChangePermissionsExecutable;
+import me.toolify.backbone.commands.ChecksumExecutable;
+import me.toolify.backbone.commands.CompressExecutable;
+import me.toolify.backbone.commands.CopyExecutable;
+import me.toolify.backbone.commands.CreateDirExecutable;
+import me.toolify.backbone.commands.CreateFileExecutable;
+import me.toolify.backbone.commands.DeleteDirExecutable;
+import me.toolify.backbone.commands.DeleteFileExecutable;
+import me.toolify.backbone.commands.DiskUsageExecutable;
+import me.toolify.backbone.commands.EchoExecutable;
+import me.toolify.backbone.commands.ExecExecutable;
+import me.toolify.backbone.commands.ExecutableCreator;
+import me.toolify.backbone.commands.FindExecutable;
+import me.toolify.backbone.commands.FolderUsageExecutable;
+import me.toolify.backbone.commands.GroupsExecutable;
+import me.toolify.backbone.commands.IdentityExecutable;
+import me.toolify.backbone.commands.LinkExecutable;
+import me.toolify.backbone.commands.ListExecutable;
+import me.toolify.backbone.commands.MountExecutable;
+import me.toolify.backbone.commands.MountPointInfoExecutable;
+import me.toolify.backbone.commands.MoveExecutable;
+import me.toolify.backbone.commands.ParentDirExecutable;
+import me.toolify.backbone.commands.ProcessIdExecutable;
+import me.toolify.backbone.commands.QuickFolderSearchExecutable;
+import me.toolify.backbone.commands.ReadExecutable;
+import me.toolify.backbone.commands.ResolveLinkExecutable;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.commands.SendSignalExecutable;
+import me.toolify.backbone.commands.UncompressExecutable;
+import me.toolify.backbone.commands.WriteExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.console.shell.ShellConsole;
+import me.toolify.backbone.model.Group;
+import me.toolify.backbone.model.MountPoint;
+import me.toolify.backbone.model.Permissions;
+import me.toolify.backbone.model.Query;
+import me.toolify.backbone.model.User;
+import me.toolify.backbone.preferences.CompressionMode;
/**
* A class for create shell {@link "Executable"} objects.
@@ -75,19 +76,6 @@ public class ShellExecutableCreator implements ExecutableCreator {
this.mConsole = console;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public ChangeCurrentDirExecutable createChangeCurrentDirExecutable(String dir)
- throws CommandNotFoundException {
- try {
- return new ChangeCurrentDirCommand(dir);
- } catch (InvalidCommandDefinitionException icdEx) {
- throw new CommandNotFoundException("ChangeCurrentDirCommand", icdEx); //$NON-NLS-1$
- }
- }
-
/**
* {@inheritDoc}
*/
@@ -153,18 +141,6 @@ public CreateFileExecutable createCreateFileExecutable(String file)
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- public CurrentDirExecutable createCurrentDirExecutable() throws CommandNotFoundException {
- try {
- return new CurrentDirCommand();
- } catch (InvalidCommandDefinitionException icdEx) {
- throw new CommandNotFoundException("CurrentDirCommand", icdEx); //$NON-NLS-1$
- }
- }
-
/**
* {@inheritDoc}
*/
@@ -396,6 +372,19 @@ public ProcessIdExecutable createShellProcessIdExecutable() throws CommandNotFou
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ProcessIdExecutable createProcessIdExecutable(int pid)
+ throws CommandNotFoundException {
+ try {
+ return new ProcessIdCommand(pid);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("ProcessIdCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -534,4 +523,19 @@ public UncompressExecutable createUncompressExecutable(
}
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ChecksumExecutable createChecksumExecutable(
+ String src, AsyncResultListener asyncResultListener)
+ throws CommandNotFoundException, NoSuchFileOrDirectory,
+ InsufficientPermissionsException {
+ try {
+ return new ChecksumCommand(src, asyncResultListener);
+ } catch (InvalidCommandDefinitionException icdEx) {
+ throw new CommandNotFoundException("ChecksumCommand", icdEx); //$NON-NLS-1$
+ }
+ }
+
}
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableFactory.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableFactory.java
similarity index 83%
rename from src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableFactory.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableFactory.java
index 52fe170b3..18d384b50 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/ShellExecutableFactory.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/ShellExecutableFactory.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.ExecutableCreator;
-import com.cyanogenmod.filemanager.commands.ExecutableFactory;
-import com.cyanogenmod.filemanager.console.shell.ShellConsole;
+import me.toolify.backbone.commands.ExecutableCreator;
+import me.toolify.backbone.commands.ExecutableFactory;
+import me.toolify.backbone.console.shell.ShellConsole;
/**
* A class that represents a factory for creating shell {@link "Executable"} objects.
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/SuperuserShell.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SuperuserShell.java
similarity index 82%
rename from src/com/cyanogenmod/filemanager/commands/shell/SuperuserShell.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/SuperuserShell.java
index d693137d6..eb970d93f 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/SuperuserShell.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SuperuserShell.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
-import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
-import com.cyanogenmod.filemanager.util.ShellHelper;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.console.NoSuchFileOrDirectory;
+import me.toolify.backbone.console.ReadOnlyFilesystemException;
+import me.toolify.backbone.util.ShellHelper;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgram.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgram.java
similarity index 97%
rename from src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgram.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgram.java
index 2d73e58c4..1d2b6c4fe 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgram.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgram.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
/**
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgramListener.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgramListener.java
similarity index 95%
rename from src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgramListener.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgramListener.java
index 56585b005..a3da068bf 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/SyncResultProgramListener.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/SyncResultProgramListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
import java.text.ParseException;
diff --git a/src/com/cyanogenmod/filemanager/commands/shell/UncompressCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/UncompressCommand.java
similarity index 81%
rename from src/com/cyanogenmod/filemanager/commands/shell/UncompressCommand.java
rename to Backbone/src/main/java/me/toolify/backbone/commands/shell/UncompressCommand.java
index 0bade8aca..34f74b5a9 100644
--- a/src/com/cyanogenmod/filemanager/commands/shell/UncompressCommand.java
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/UncompressCommand.java
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.commands.shell;
+package me.toolify.backbone.commands.shell;
-import com.cyanogenmod.filemanager.commands.AsyncResultListener;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
-import com.cyanogenmod.filemanager.commands.UncompressExecutable;
-import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.ExecutionException;
-import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
-import com.cyanogenmod.filemanager.preferences.UncompressionMode;
-import com.cyanogenmod.filemanager.util.FileHelper;
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.commands.UncompressExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+import me.toolify.backbone.preferences.UncompressionMode;
+import me.toolify.backbone.util.FileHelper;
import java.io.File;
@@ -85,7 +85,11 @@ private enum Mode {
/**
* Uncompress using Unix compress algorithm
*/
- C_UNXZ(UNXZ_ID, "", UncompressionMode.C_UNXZ); //$NON-NLS-1$
+ C_UNXZ(UNXZ_ID, "", UncompressionMode.C_UNXZ), //$NON-NLS-1$
+ /**
+ * Uncompress using Rar algorithm
+ */
+ A_UNRAR(UNRAR_ID, "", UncompressionMode.C_UNRAR); //$NON-NLS-1$
final String mId;
final String mFlag;
@@ -112,12 +116,13 @@ private Mode(String id, String flag, UncompressionMode mode) {
private static final String UNLZMA_ID = "unlzma"; //$NON-NLS-1$
private static final String UNCOMPRESS_ID = "uncompress"; //$NON-NLS-1$
private static final String UNXZ_ID = "unxz"; //$NON-NLS-1$
+ private static final String UNRAR_ID = "unrar"; //$NON-NLS-1$
private Boolean mResult;
private String mPartial;
private final String mOutFile;
- private final boolean mIsArchive;
+ private final Mode mMode;
/**
* Constructor of UncompressCommand.
@@ -146,6 +151,7 @@ public UncompressCommand(
throw new InvalidCommandDefinitionException(
"Unsupported uncompress mode"); //$NON-NLS-1$
}
+ this.mMode = mode;
// Retrieve information about the uncompress process
if (dst != null) {
@@ -153,7 +159,6 @@ public UncompressCommand(
} else {
this.mOutFile = resolveOutputFile(src);
}
- this.mIsArchive = mode.mMode.mArchive;
}
/**
@@ -173,7 +178,10 @@ public void onEndParsePartialResult(boolean cancelled) {
// Send the last partial data
if (this.mPartial != null && this.mPartial.length() > 0) {
if (getAsyncResultListener() != null) {
- getAsyncResultListener().onPartialResult(this.mPartial);
+ String data = processPartialResult(this.mPartial);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
}
}
this.mPartial = ""; //$NON-NLS-1$
@@ -194,14 +202,20 @@ public void onParsePartialResult(final String partialIn) {
// Return all the lines, except the last
for (int i = 0; i < lines.length-1; i++) {
if (getAsyncResultListener() != null) {
- getAsyncResultListener().onPartialResult(lines[i]);
+ String data = processPartialResult(lines[i]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
}
}
// Return the last line?
if (endsWithNewLine) {
if (getAsyncResultListener() != null) {
- getAsyncResultListener().onPartialResult(lines[lines.length-1]);
+ String data = processPartialResult(lines[lines.length-1]);
+ if (data != null) {
+ getAsyncResultListener().onPartialResult(data);
+ }
}
this.mPartial = ""; //$NON-NLS-1$
} else {
@@ -263,7 +277,36 @@ public String getOutUncompressedFile() {
*/
@Override
public boolean IsArchive() {
- return this.mIsArchive;
+ return this.mMode.mMode.mArchive;
+ }
+
+ /**
+ * Method that processes a line to determine if it's a valid partial result
+ *
+ * @param line The line to process
+ * @return String The processed line
+ */
+ private String processPartialResult(String line) {
+ if (this.mMode.compareTo(Mode.A_UNRAR) == 0) {
+ if (line.startsWith("Extracting ")) { //$NON-NLS-1$
+ int pos = line.indexOf((char)8);
+ if (pos != -1) {
+ // Remove progress
+ return line.substring(12, pos).trim();
+ }
+ return line.substring(12).trim();
+ }
+ return null;
+ }
+
+ if (this.mMode.compareTo(Mode.A_UNZIP) == 0) {
+ if (line.startsWith(" inflating: ")) { //$NON-NLS-1$
+ return line.substring(13).trim();
+ }
+ return null;
+ }
+
+ return line;
}
/**
@@ -303,6 +346,7 @@ private static String[] resolveArguments(String src, String dst) {
return new String[]{mode.mFlag, out, src};
case A_UNZIP:
+ case A_UNRAR:
return new String[]{out, src};
case C_GUNZIP:
diff --git a/Backbone/src/main/java/me/toolify/backbone/commands/shell/WriteCommand.java b/Backbone/src/main/java/me/toolify/backbone/commands/shell/WriteCommand.java
new file mode 100644
index 000000000..62160cb95
--- /dev/null
+++ b/Backbone/src/main/java/me/toolify/backbone/commands/shell/WriteCommand.java
@@ -0,0 +1,161 @@
+/*
+ * 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 me.toolify.backbone.commands.shell;
+
+import me.toolify.backbone.commands.AsyncResultListener;
+import me.toolify.backbone.commands.SIGNAL;
+import me.toolify.backbone.commands.WriteExecutable;
+import me.toolify.backbone.console.CommandNotFoundException;
+import me.toolify.backbone.console.ExecutionException;
+import me.toolify.backbone.console.InsufficientPermissionsException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * A class for write data to disk.
+ *
+ * User MUST call the {@link #createOutputStream()} to get the output stream where
+ * write the data.
. When no more exist then user MUST call the onEnd method
+ * of the asynchronous listener.
+ *
+ * {@link "http://unixhelp.ed.ac.uk/CGI/man-cgi?dd"}
+ */
+public class WriteCommand extends AsyncResultProgram implements WriteExecutable {
+
+ private static final String ID = "write"; //$NON-NLS-1$
+
+ private static final long TIMEOUT = 1000L;
+
+ /**
+ * @hide
+ */
+ final Object mWriteSync = new Object();
+ private boolean mReady;
+ /**
+ * @hide
+ */
+ boolean mError;
+
+ /**
+ * Constructor of WriteCommand.
+ *
+ * @param file The file where to write the data
+ * @param asyncResultListener The partial result listener
+ * @throws InvalidCommandDefinitionException If the command has an invalid definition
+ */
+ public WriteCommand(
+ String file, AsyncResultListener asyncResultListener)
+ throws InvalidCommandDefinitionException {
+ super(ID, asyncResultListener, file);
+ this.mReady = false;
+ this.mError = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isExpectEnd() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public OutputStream createOutputStream() throws IOException {
+
+ // Wait until command is ready
+ synchronized (this.mWriteSync) {
+ if (!this.mReady) {
+ try {
+ this.mWriteSync.wait(TIMEOUT);
+ } catch (Exception e) {/**NON BLOCK**/}
+ }
+ }
+ return getProgramListener().getOutputStream();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onStartParsePartialResult() {
+ synchronized (this.mWriteSync) {
+ this.mReady = true;
+ this.mWriteSync.notify();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onEndParsePartialResult(boolean cancelled) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SIGNAL onRequestEnd() {
+ try {
+ if (this.getProgramListener().getOutputStream() != null) {
+ this.getProgramListener().getOutputStream().flush();
+ }
+ } catch (Exception ex) {/**NON BLOCK**/}
+ try {
+ Thread.yield();
+ } catch (Exception ex) {/**NON BLOCK**/}
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParsePartialResult(final String partialIn) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onParseErrorPartialResult(String partialErr) {/**NON BLOCK**/}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isIgnoreShellStdErrCheck() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkExitCode(int exitCode)
+ throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException {
+ //Ignore exit code 143 (cancelled)
+ //Ignore exit code 137 (kill -9)
+ if (exitCode != 0 && exitCode != 143 && exitCode != 137) {
+ throw new ExecutionException(
+ "exitcode != 0 && != 143 && != 137"); //$NON-NLS-1$
+ }
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/console/CommandNotFoundException.java b/Backbone/src/main/java/me/toolify/backbone/console/CommandNotFoundException.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/console/CommandNotFoundException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/CommandNotFoundException.java
index cca7db2a3..b8d725aee 100644
--- a/src/com/cyanogenmod/filemanager/console/CommandNotFoundException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/CommandNotFoundException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* An exception thrown when the command was not found
diff --git a/src/com/cyanogenmod/filemanager/console/Console.java b/Backbone/src/main/java/me/toolify/backbone/console/Console.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/console/Console.java
rename to Backbone/src/main/java/me/toolify/backbone/console/Console.java
index ba28db55a..1232a9a04 100644
--- a/src/com/cyanogenmod/filemanager/console/Console.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/Console.java
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
-
-import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
-import com.cyanogenmod.filemanager.commands.Executable;
-import com.cyanogenmod.filemanager.commands.ExecutableFactory;
-import com.cyanogenmod.filemanager.model.Identity;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.Preferences;
+package me.toolify.backbone.console;
+
+import me.toolify.backbone.commands.AsyncResultExecutable;
+import me.toolify.backbone.commands.Executable;
+import me.toolify.backbone.commands.ExecutableFactory;
+import me.toolify.backbone.model.Identity;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
/**
* This class represents a class for executing commands in the operating system layer,
diff --git a/src/com/cyanogenmod/filemanager/console/ConsoleAllocException.java b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleAllocException.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/console/ConsoleAllocException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/ConsoleAllocException.java
index ade454a63..7ad3d474e 100644
--- a/src/com/cyanogenmod/filemanager/console/ConsoleAllocException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleAllocException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* An exception thrown when an exception occurs allocating a console.
diff --git a/src/com/cyanogenmod/filemanager/console/ConsoleBuilder.java b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleBuilder.java
similarity index 85%
rename from src/com/cyanogenmod/filemanager/console/ConsoleBuilder.java
rename to Backbone/src/main/java/me/toolify/backbone/console/ConsoleBuilder.java
index 1552abd95..560123a30 100644
--- a/src/com/cyanogenmod/filemanager/console/ConsoleBuilder.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleBuilder.java
@@ -14,23 +14,22 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
-import com.cyanogenmod.filemanager.FileManagerApplication;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.commands.shell.InvalidCommandDefinitionException;
-import com.cyanogenmod.filemanager.console.java.JavaConsole;
-import com.cyanogenmod.filemanager.console.shell.NonPriviledgeConsole;
-import com.cyanogenmod.filemanager.console.shell.PrivilegedConsole;
-import com.cyanogenmod.filemanager.preferences.AccessMode;
-import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
-import com.cyanogenmod.filemanager.preferences.Preferences;
-import com.cyanogenmod.filemanager.util.DialogHelper;
-import com.cyanogenmod.filemanager.util.FileHelper;
+import me.toolify.backbone.FileManagerApplication;
+import me.toolify.backbone.R;
+import me.toolify.backbone.commands.shell.InvalidCommandDefinitionException;
+import me.toolify.backbone.console.java.JavaConsole;
+import me.toolify.backbone.console.shell.NonPriviledgeConsole;
+import me.toolify.backbone.console.shell.PrivilegedConsole;
+import me.toolify.backbone.preferences.AccessMode;
+import me.toolify.backbone.preferences.FileManagerSettings;
+import me.toolify.backbone.preferences.Preferences;
+import me.toolify.backbone.util.DialogHelper;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -95,13 +94,6 @@ public static Console getConsole(Context context, boolean createIfNotExists)
return null;
}
createDefaultConsole(context);
- } else {
- // Need to change the console? Is the appropriate console for the current mode?
- if (FileManagerApplication.getAccessMode().
- compareTo(AccessMode.ROOT) == 0 && !isPrivileged()) {
- // Force to change the console
- createDefaultConsole(context);
- }
}
return sHolder.getConsole();
}
@@ -125,7 +117,7 @@ public static boolean changeToNonPrivilegedConsole(Context context) {
try {
//Create the console, destroy the current console, and marks as current
holder = new ConsoleHolder(
- createNonPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
+ createNonPrivilegedConsole(context));
destroyConsole();
sHolder = holder;
return true;
@@ -157,7 +149,7 @@ public static boolean changeToPrivilegedConsole(Context context) {
try {
//Create the console, destroy the current console, and marks as current
holder = new ConsoleHolder(
- createAndCheckPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
+ createAndCheckPrivilegedConsole(context));
destroyConsole();
sHolder = holder;
@@ -243,11 +235,8 @@ public static Console createDefaultConsole(Context context,
//Is there a console allocated
if (sHolder == null) {
sHolder = (superuserMode)
- ? new ConsoleHolder(
- createAndCheckPrivilegedConsole(
- context, FileHelper.ROOT_DIRECTORY))
- : new ConsoleHolder(
- createNonPrivilegedConsole(context, FileHelper.ROOT_DIRECTORY));
+ ? new ConsoleHolder(createAndCheckPrivilegedConsole(context))
+ : new ConsoleHolder(createNonPrivilegedConsole(context));
if (superuserMode) {
// Change also the background console to privileged
FileManagerApplication.changeBackgroundConsoleToPriviligedConsole();
@@ -275,7 +264,6 @@ public static void destroyConsole() {
* Method that creates a new non privileged console.
*
* @param context The current context
- * @param initialDirectory The initial directory of the console
* @return Console The non privileged console
* @throws FileNotFoundException If the initial directory not exists
* @throws IOException If initial directory couldn't be checked
@@ -283,7 +271,7 @@ public static void destroyConsole() {
* @throws ConsoleAllocException If the console can't be allocated
* @see NonPriviledgeConsole
*/
- public static Console createNonPrivilegedConsole(Context context, String initialDirectory)
+ public static Console createNonPrivilegedConsole(Context context)
throws FileNotFoundException, IOException,
InvalidCommandDefinitionException, ConsoleAllocException {
@@ -291,14 +279,14 @@ public static Console createNonPrivilegedConsole(Context context, String initial
// Is rooted? Then create a shell console
if (FileManagerApplication.isDeviceRooted()) {
- NonPriviledgeConsole console = new NonPriviledgeConsole(initialDirectory);
+ NonPriviledgeConsole console = new NonPriviledgeConsole();
console.setBufferSize(bufferSize);
console.alloc();
return console;
}
// No rooted. Then create a java console
- JavaConsole console = new JavaConsole(context, initialDirectory, bufferSize);
+ JavaConsole console = new JavaConsole(context, bufferSize);
console.alloc();
return console;
}
@@ -308,7 +296,6 @@ public static Console createNonPrivilegedConsole(Context context, String initial
* privileged console fails, the a non privileged console
*
* @param context The current context
- * @param initialDirectory The initial directory of the console
* @return Console The privileged console
* @throws FileNotFoundException If the initial directory not exists
* @throws IOException If initial directory couldn't be checked
@@ -317,10 +304,10 @@ public static Console createNonPrivilegedConsole(Context context, String initial
* @throws InsufficientPermissionsException If the console created is not a privileged console
* @see PrivilegedConsole
*/
- public static Console createPrivilegedConsole(Context context, String initialDirectory)
+ public static Console createPrivilegedConsole(Context context)
throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
ConsoleAllocException, InsufficientPermissionsException {
- PrivilegedConsole console = new PrivilegedConsole(initialDirectory);
+ PrivilegedConsole console = new PrivilegedConsole();
console.setBufferSize(context.getResources().getInteger(R.integer.buffer_size));
console.alloc();
if (console.getIdentity().getUser().getId() != ROOT_UID) {
@@ -340,7 +327,6 @@ public static Console createPrivilegedConsole(Context context, String initialDir
* privileged console fails, the a non privileged console
*
* @param context The current context
- * @param initialDirectory The initial directory of the console
* @return Console The privileged console
* @throws FileNotFoundException If the initial directory not exists
* @throws IOException If initial directory couldn't be checked
@@ -349,10 +335,10 @@ public static Console createPrivilegedConsole(Context context, String initialDir
* @throws InsufficientPermissionsException If the console created is not a privileged console
* @see PrivilegedConsole
*/
- public static Console createAndCheckPrivilegedConsole(Context context, String initialDirectory)
+ public static Console createAndCheckPrivilegedConsole(Context context)
throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
ConsoleAllocException, InsufficientPermissionsException {
- return createAndCheckPrivilegedConsole(context, initialDirectory, true);
+ return createAndCheckPrivilegedConsole(context, true);
}
/**
@@ -360,7 +346,6 @@ public static Console createAndCheckPrivilegedConsole(Context context, String in
* privileged console fails, the a non privileged console
*
* @param context The current context
- * @param initialDirectory The initial directory of the console
* @param silent Indicates that no message have to be displayed
* @return Console The privileged console
* @throws FileNotFoundException If the initial directory not exists
@@ -371,12 +356,12 @@ public static Console createAndCheckPrivilegedConsole(Context context, String in
* @see PrivilegedConsole
*/
public static Console createAndCheckPrivilegedConsole(
- Context context, String initialDirectory, boolean silent)
+ Context context, boolean silent)
throws FileNotFoundException, IOException, InvalidCommandDefinitionException,
ConsoleAllocException, InsufficientPermissionsException {
try {
// Create the privileged console
- return createPrivilegedConsole(context, initialDirectory);
+ return createPrivilegedConsole(context);
} catch (ConsoleAllocException caEx) {
//Show a message with the problem?
@@ -404,7 +389,7 @@ public static Console createAndCheckPrivilegedConsole(
}
//Create the non-privileged console
- return createNonPrivilegedConsole(context, initialDirectory);
+ return createNonPrivilegedConsole(context);
}
// Rethrow the exception
@@ -412,18 +397,6 @@ public static Console createAndCheckPrivilegedConsole(
}
}
- /**
- * Method that returns if the current console is a privileged console
- *
- * @return boolean If the current console is a privileged console
- */
- public static boolean isAlloc() {
- if (sHolder != null && sHolder.getConsole() != null) {
- return true;
- }
- return false;
- }
-
/**
* Method that returns if the current console is a privileged console
*
diff --git a/src/com/cyanogenmod/filemanager/console/ConsoleHolder.java b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleHolder.java
similarity index 97%
rename from src/com/cyanogenmod/filemanager/console/ConsoleHolder.java
rename to Backbone/src/main/java/me/toolify/backbone/console/ConsoleHolder.java
index cb3e86867..06a3227d3 100644
--- a/src/com/cyanogenmod/filemanager/console/ConsoleHolder.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/ConsoleHolder.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* A class that holds a console.
diff --git a/src/com/cyanogenmod/filemanager/console/ExecutionException.java b/Backbone/src/main/java/me/toolify/backbone/console/ExecutionException.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/console/ExecutionException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/ExecutionException.java
index e05d35e28..d5bbb8acb 100644
--- a/src/com/cyanogenmod/filemanager/console/ExecutionException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/ExecutionException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* An exception thrown when an operation invocation fails.
diff --git a/src/com/cyanogenmod/filemanager/console/InsufficientPermissionsException.java b/Backbone/src/main/java/me/toolify/backbone/console/InsufficientPermissionsException.java
similarity index 90%
rename from src/com/cyanogenmod/filemanager/console/InsufficientPermissionsException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/InsufficientPermissionsException.java
index 614e5609e..26e7bd126 100644
--- a/src/com/cyanogenmod/filemanager/console/InsufficientPermissionsException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/InsufficientPermissionsException.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
-import com.cyanogenmod.filemanager.R;
-import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
+import me.toolify.backbone.R;
+import me.toolify.backbone.commands.SyncResultExecutable;
/**
* An exception thrown when an operation required elevated permissions.
diff --git a/src/com/cyanogenmod/filemanager/console/NoSuchFileOrDirectory.java b/Backbone/src/main/java/me/toolify/backbone/console/NoSuchFileOrDirectory.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/console/NoSuchFileOrDirectory.java
rename to Backbone/src/main/java/me/toolify/backbone/console/NoSuchFileOrDirectory.java
index a2e87c7b8..0a334d680 100644
--- a/src/com/cyanogenmod/filemanager/console/NoSuchFileOrDirectory.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/NoSuchFileOrDirectory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* An exception thrown when the file or directory is not found.
diff --git a/src/com/cyanogenmod/filemanager/console/OperationTimeoutException.java b/Backbone/src/main/java/me/toolify/backbone/console/OperationTimeoutException.java
similarity index 96%
rename from src/com/cyanogenmod/filemanager/console/OperationTimeoutException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/OperationTimeoutException.java
index c549fda00..fa8b9c949 100644
--- a/src/com/cyanogenmod/filemanager/console/OperationTimeoutException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/OperationTimeoutException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
/**
* An exception thrown when the operation exceeds the timeout.
diff --git a/src/com/cyanogenmod/filemanager/console/ReadOnlyFilesystemException.java b/Backbone/src/main/java/me/toolify/backbone/console/ReadOnlyFilesystemException.java
similarity index 93%
rename from src/com/cyanogenmod/filemanager/console/ReadOnlyFilesystemException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/ReadOnlyFilesystemException.java
index 6e26146bd..fec09de16 100644
--- a/src/com/cyanogenmod/filemanager/console/ReadOnlyFilesystemException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/ReadOnlyFilesystemException.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
-import com.cyanogenmod.filemanager.model.MountPoint;
+import me.toolify.backbone.model.MountPoint;
/**
* An exception thrown when an operation is writing in a read-only filesystem.
diff --git a/src/com/cyanogenmod/filemanager/console/RelaunchableException.java b/Backbone/src/main/java/me/toolify/backbone/console/RelaunchableException.java
similarity index 88%
rename from src/com/cyanogenmod/filemanager/console/RelaunchableException.java
rename to Backbone/src/main/java/me/toolify/backbone/console/RelaunchableException.java
index db5ece7bb..3d72a74c0 100644
--- a/src/com/cyanogenmod/filemanager/console/RelaunchableException.java
+++ b/Backbone/src/main/java/me/toolify/backbone/console/RelaunchableException.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.cyanogenmod.filemanager.console;
+package me.toolify.backbone.console;
import android.os.AsyncTask;
-import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
+import me.toolify.backbone.commands.SyncResultExecutable;
import java.util.ArrayList;
import java.util.List;
@@ -41,7 +41,9 @@ public abstract class RelaunchableException extends Exception {
public RelaunchableException(SyncResultExecutable executable) {
super();
this.mExecutables = new ArrayList();
- addExecutable(executable);
+ if (executable != null) {
+ addExecutable(executable);
+ }
}
/**
@@ -53,7 +55,9 @@ public RelaunchableException(SyncResultExecutable executable) {
public RelaunchableException(String detailMessage, SyncResultExecutable executable) {
super(detailMessage);
this.mExecutables = new ArrayList();
- addExecutable(executable);
+ if (executable != null) {
+ addExecutable(executable);
+ }
}
/**
@@ -67,7 +71,9 @@ public RelaunchableException(
String detailMessage, Throwable throwable, SyncResultExecutable executable) {
super(detailMessage, throwable);
this.mExecutables = new ArrayList();
- addExecutable(executable);
+ if (executable != null) {
+ addExecutable(executable);
+ }
}
/**
@@ -91,7 +97,7 @@ public void addExecutable(SyncResultExecutable executable) {
/**
* Method that returns the task to execute when the re-execution ends.
*
- * @return AsyncTask