Great Update to the source
This commit is contained in:
9
libraries/FloatingActionButton/.classpath
Normal file
9
libraries/FloatingActionButton/.classpath
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
33
libraries/FloatingActionButton/.project
Normal file
33
libraries/FloatingActionButton/.project
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>FloatingActionButton</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@@ -0,0 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
12
libraries/FloatingActionButton/AndroidManifest.xml
Normal file
12
libraries/FloatingActionButton/AndroidManifest.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.faizmalkani.floatingactionbutton">
|
||||
|
||||
<application
|
||||
android:allowBackup="true">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
28
libraries/FloatingActionButton/build.gradle
Normal file
28
libraries/FloatingActionButton/build.gradle
Normal file
@@ -0,0 +1,28 @@
|
||||
apply plugin: 'com.android.library'
|
||||
android {
|
||||
compileSdkVersion 19
|
||||
buildToolsVersion "20.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.faizmalkani.floatingactionbutton'
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 19
|
||||
versionName "1.0"
|
||||
versionCode 1
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Automatically generated file. DO NOT MODIFY
|
||||
*/
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
public final class BuildConfig {
|
||||
public static final boolean DEBUG = Boolean.parseBoolean("true");
|
||||
public static final String PACKAGE_NAME = "com.faizmalkani.floatingactionbutton";
|
||||
public static final String BUILD_TYPE = "debug";
|
||||
public static final String FLAVOR = "";
|
||||
public static final int VERSION_CODE = 1;
|
||||
public static final String VERSION_NAME = "1.0";
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Automatically generated file. DO NOT MODIFY
|
||||
*/
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
public final class BuildConfig {
|
||||
public static final boolean DEBUG = false;
|
||||
public static final String PACKAGE_NAME = "com.faizmalkani.floatingactionbutton";
|
||||
public static final String BUILD_TYPE = "release";
|
||||
public static final String FLAVOR = "";
|
||||
public static final int VERSION_CODE = 1;
|
||||
public static final String VERSION_NAME = "1.0";
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Automatically generated file. DO NOT MODIFY
|
||||
*/
|
||||
package com.faizmalkani.floatingactionbutton.test;
|
||||
|
||||
public final class BuildConfig {
|
||||
public static final boolean DEBUG = Boolean.parseBoolean("true");
|
||||
public static final String PACKAGE_NAME = "com.faizmalkani.floatingactionbutton.test";
|
||||
public static final String BUILD_TYPE = "debug";
|
||||
public static final String FLAVOR = "";
|
||||
public static final int VERSION_CODE = 1;
|
||||
public static final String VERSION_NAME = "1.0";
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int color=0x7f010001;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int drawable=0x7f010000;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowColor=0x7f010005;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowDx=0x7f010003;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowDy=0x7f010004;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowRadius=0x7f010002;
|
||||
}
|
||||
public static final class styleable {
|
||||
/** Attributes that can be used with a FloatingActionButton.
|
||||
<p>Includes the following attributes:</p>
|
||||
<table>
|
||||
<colgroup align="left" />
|
||||
<colgroup align="left" />
|
||||
<tr><th>Attribute</th><th>Description</th></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_color com.faizmalkani.floatingactionbutton:color}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}</code></td><td></td></tr>
|
||||
</table>
|
||||
@see #FloatingActionButton_color
|
||||
@see #FloatingActionButton_drawable
|
||||
@see #FloatingActionButton_shadowColor
|
||||
@see #FloatingActionButton_shadowDx
|
||||
@see #FloatingActionButton_shadowDy
|
||||
@see #FloatingActionButton_shadowRadius
|
||||
*/
|
||||
public static final int[] FloatingActionButton = {
|
||||
0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
|
||||
0x7f010004, 0x7f010005
|
||||
};
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#color}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:color
|
||||
*/
|
||||
public static final int FloatingActionButton_color = 1;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:drawable
|
||||
*/
|
||||
public static final int FloatingActionButton_drawable = 0;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowColor
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowColor = 5;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowDx
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDx = 3;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowDy
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDy = 4;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowRadius
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowRadius = 2;
|
||||
};
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int color=0x7f010001;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int drawable=0x7f010000;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowColor=0x7f010005;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowDx=0x7f010003;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowDy=0x7f010004;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static int shadowRadius=0x7f010002;
|
||||
}
|
||||
public static final class styleable {
|
||||
/** Attributes that can be used with a FloatingActionButton.
|
||||
<p>Includes the following attributes:</p>
|
||||
<table>
|
||||
<colgroup align="left" />
|
||||
<colgroup align="left" />
|
||||
<tr><th>Attribute</th><th>Description</th></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_color com.faizmalkani.floatingactionbutton:color}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}</code></td><td></td></tr>
|
||||
</table>
|
||||
@see #FloatingActionButton_color
|
||||
@see #FloatingActionButton_drawable
|
||||
@see #FloatingActionButton_shadowColor
|
||||
@see #FloatingActionButton_shadowDx
|
||||
@see #FloatingActionButton_shadowDy
|
||||
@see #FloatingActionButton_shadowRadius
|
||||
*/
|
||||
public static final int[] FloatingActionButton = {
|
||||
0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
|
||||
0x7f010004, 0x7f010005
|
||||
};
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#color}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:color
|
||||
*/
|
||||
public static final int FloatingActionButton_color = 1;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:drawable
|
||||
*/
|
||||
public static final int FloatingActionButton_drawable = 0;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowColor
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowColor = 5;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowDx
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDx = 3;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowDy
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDy = 4;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton:shadowRadius
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowRadius = 2;
|
||||
};
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
public static final int color = 0x7f010001;
|
||||
public static final int drawable = 0x7f010000;
|
||||
public static final int shadowColor = 0x7f010005;
|
||||
public static final int shadowDx = 0x7f010003;
|
||||
public static final int shadowDy = 0x7f010004;
|
||||
public static final int shadowRadius = 0x7f010002;
|
||||
}
|
||||
public static final class styleable {
|
||||
public static final int[] FloatingActionButton = { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 };
|
||||
public static final int FloatingActionButton_color = 1;
|
||||
public static final int FloatingActionButton_drawable = 0;
|
||||
public static final int FloatingActionButton_shadowColor = 5;
|
||||
public static final int FloatingActionButton_shadowDx = 3;
|
||||
public static final int FloatingActionButton_shadowDy = 4;
|
||||
public static final int FloatingActionButton_shadowRadius = 2;
|
||||
}
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.faizmalkani.floatingactionbutton.test;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int color=0x7f010001;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int drawable=0x7f010000;
|
||||
/** <p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int shadowColor=0x7f010005;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int shadowDx=0x7f010003;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int shadowDy=0x7f010004;
|
||||
/** <p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
*/
|
||||
public static final int shadowRadius=0x7f010002;
|
||||
}
|
||||
public static final class styleable {
|
||||
/** Attributes that can be used with a FloatingActionButton.
|
||||
<p>Includes the following attributes:</p>
|
||||
<table>
|
||||
<colgroup align="left" />
|
||||
<colgroup align="left" />
|
||||
<tr><th>Attribute</th><th>Description</th></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_color com.faizmalkani.floatingactionbutton.test:color}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton.test:drawable}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton.test:shadowColor}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton.test:shadowDx}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton.test:shadowDy}</code></td><td></td></tr>
|
||||
<tr><td><code>{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton.test:shadowRadius}</code></td><td></td></tr>
|
||||
</table>
|
||||
@see #FloatingActionButton_color
|
||||
@see #FloatingActionButton_drawable
|
||||
@see #FloatingActionButton_shadowColor
|
||||
@see #FloatingActionButton_shadowDx
|
||||
@see #FloatingActionButton_shadowDy
|
||||
@see #FloatingActionButton_shadowRadius
|
||||
*/
|
||||
public static final int[] FloatingActionButton = {
|
||||
0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
|
||||
0x7f010004, 0x7f010005
|
||||
};
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#color}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
|
||||
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:color
|
||||
*/
|
||||
public static final int FloatingActionButton_color = 1;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#drawable}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:drawable
|
||||
*/
|
||||
public static final int FloatingActionButton_drawable = 0;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowColor}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be an integer value, such as "<code>100</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:shadowColor
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowColor = 5;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDx}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:shadowDx
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDx = 3;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDy}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:shadowDy
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowDy = 4;
|
||||
/**
|
||||
<p>This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowRadius}
|
||||
attribute's value can be found in the {@link #FloatingActionButton} array.
|
||||
|
||||
|
||||
<p>Must be a floating point value, such as "<code>1.2</code>".
|
||||
<p>This may also be a reference to a resource (in the form
|
||||
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
|
||||
theme attribute (in the form
|
||||
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
|
||||
containing a value of this type.
|
||||
@attr name com.faizmalkani.floatingactionbutton.test:shadowRadius
|
||||
*/
|
||||
public static final int FloatingActionButton_shadowRadius = 2;
|
||||
};
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.faizmalkani.floatingactionbutton"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application android:allowBackup="true" >
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -0,0 +1,13 @@
|
||||
int attr color 0x7f010001
|
||||
int attr drawable 0x7f010000
|
||||
int attr shadowColor 0x7f010005
|
||||
int attr shadowDx 0x7f010003
|
||||
int attr shadowDy 0x7f010004
|
||||
int attr shadowRadius 0x7f010002
|
||||
int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 }
|
||||
int styleable FloatingActionButton_color 1
|
||||
int styleable FloatingActionButton_drawable 0
|
||||
int styleable FloatingActionButton_shadowColor 5
|
||||
int styleable FloatingActionButton_shadowDx 3
|
||||
int styleable FloatingActionButton_shadowDy 4
|
||||
int styleable FloatingActionButton_shadowRadius 2
|
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- From: file:/C:/Users/Vlad/Documents/AndroidStudioProjects/turbo-editor/libraries/FloatingActionButton/res/values/attrs.xml -->
|
||||
<eat-comment />
|
||||
|
||||
<declare-styleable name="FloatingActionButton">
|
||||
<attr name="drawable" format="integer" />
|
||||
<attr name="color" format="color" />
|
||||
<attr name="shadowRadius" format="float" />
|
||||
<attr name="shadowDx" format="float" />
|
||||
<attr name="shadowDy" format="float" />
|
||||
<attr name="shadowColor" format="integer" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.faizmalkani.floatingactionbutton"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<application android:allowBackup="true" >
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -0,0 +1,13 @@
|
||||
int attr color 0x7f010001
|
||||
int attr drawable 0x7f010000
|
||||
int attr shadowColor 0x7f010005
|
||||
int attr shadowDx 0x7f010003
|
||||
int attr shadowDy 0x7f010004
|
||||
int attr shadowRadius 0x7f010002
|
||||
int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 }
|
||||
int styleable FloatingActionButton_color 1
|
||||
int styleable FloatingActionButton_drawable 0
|
||||
int styleable FloatingActionButton_shadowColor 5
|
||||
int styleable FloatingActionButton_shadowDx 3
|
||||
int styleable FloatingActionButton_shadowDy 4
|
||||
int styleable FloatingActionButton_shadowRadius 2
|
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- From: file:/C:/Users/Vlad/Documents/AndroidStudioProjects/turbo-editor/libraries/FloatingActionButton/res/values/attrs.xml -->
|
||||
<eat-comment />
|
||||
|
||||
<declare-styleable name="FloatingActionButton">
|
||||
<attr name="drawable" format="integer" />
|
||||
<attr name="color" format="color" />
|
||||
<attr name="shadowRadius" format="float" />
|
||||
<attr name="shadowDx" format="float" />
|
||||
<attr name="shadowDy" format="float" />
|
||||
<attr name="shadowColor" format="integer" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\assets" />
|
||||
</dataSet>
|
||||
<dataSet config="debug" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\debug\assets" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\assets" />
|
||||
</dataSet>
|
||||
<dataSet config="release" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\release\assets" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="debug" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\intermediates\bundles\debug\assets" />
|
||||
</dataSet>
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\androidTest\assets" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="debug" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\intermediates\bundles\debug\res" >
|
||||
<file
|
||||
path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\intermediates\bundles\debug\res\values\values.xml"
|
||||
qualifiers="" >
|
||||
<declare-styleable name="FloatingActionButton" >
|
||||
<attr
|
||||
name="drawable"
|
||||
format="integer" />
|
||||
<attr
|
||||
name="color"
|
||||
format="color" />
|
||||
<attr
|
||||
name="shadowRadius"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDx"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDy"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowColor"
|
||||
format="integer" />
|
||||
</declare-styleable>
|
||||
</file>
|
||||
</source>
|
||||
</dataSet>
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\androidTest\res" />
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\rs\test\debug" />
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\generated\test\debug" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\res" >
|
||||
<file
|
||||
path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\res\values\attrs.xml"
|
||||
qualifiers="" >
|
||||
<declare-styleable name="FloatingActionButton" >
|
||||
<attr
|
||||
name="drawable"
|
||||
format="integer" />
|
||||
<attr
|
||||
name="color"
|
||||
format="color" />
|
||||
<attr
|
||||
name="shadowRadius"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDx"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDy"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowColor"
|
||||
format="integer" />
|
||||
</declare-styleable>
|
||||
</file>
|
||||
</source>
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\rs\debug" />
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\generated\debug" />
|
||||
</dataSet>
|
||||
<dataSet config="debug" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\debug\res" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merger version="2" >
|
||||
|
||||
<dataSet config="main" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\res" >
|
||||
<file
|
||||
path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\res\values\attrs.xml"
|
||||
qualifiers="" >
|
||||
<declare-styleable name="FloatingActionButton" >
|
||||
<attr
|
||||
name="drawable"
|
||||
format="integer" />
|
||||
<attr
|
||||
name="color"
|
||||
format="color" />
|
||||
<attr
|
||||
name="shadowRadius"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDx"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowDy"
|
||||
format="float" />
|
||||
<attr
|
||||
name="shadowColor"
|
||||
format="integer" />
|
||||
</declare-styleable>
|
||||
</file>
|
||||
</source>
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\rs\release" />
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\build\generated\res\generated\release" />
|
||||
</dataSet>
|
||||
<dataSet config="release" >
|
||||
<source path="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\libraries\FloatingActionButton\src\release\res" />
|
||||
</dataSet>
|
||||
|
||||
</merger>
|
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.faizmalkani.floatingactionbutton.test" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<instrumentation
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
android:functionalTest="false"
|
||||
android:handleProfiling="false"
|
||||
android:label="Tests for com.faizmalkani.floatingactionbutton.test"
|
||||
android:targetPackage="com.faizmalkani.floatingactionbutton.test" />
|
||||
|
||||
<application android:allowBackup="true" >
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!-- From: file:/C:/Users/Vlad/Documents/AndroidStudioProjects/turbo-editor/libraries/FloatingActionButton/build/intermediates/bundles/debug/res/values/values.xml -->
|
||||
<eat-comment />
|
||||
|
||||
<declare-styleable name="FloatingActionButton">
|
||||
<attr name="drawable" format="integer" />
|
||||
<attr name="color" format="color" />
|
||||
<attr name="shadowRadius" format="float" />
|
||||
<attr name="shadowDx" format="float" />
|
||||
<attr name="shadowDy" format="float" />
|
||||
<attr name="shadowColor" format="integer" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@@ -0,0 +1,13 @@
|
||||
int attr color 0x7f010001
|
||||
int attr drawable 0x7f010000
|
||||
int attr shadowColor 0x7f010005
|
||||
int attr shadowDx 0x7f010003
|
||||
int attr shadowDy 0x7f010004
|
||||
int attr shadowRadius 0x7f010002
|
||||
int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 }
|
||||
int styleable FloatingActionButton_color 1
|
||||
int styleable FloatingActionButton_drawable 0
|
||||
int styleable FloatingActionButton_shadowColor 5
|
||||
int styleable FloatingActionButton_shadowDx 3
|
||||
int styleable FloatingActionButton_shadowDy 4
|
||||
int styleable FloatingActionButton_shadowRadius 2
|
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
|
20
libraries/FloatingActionButton/proguard-project.txt
Normal file
20
libraries/FloatingActionButton/proguard-project.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
16
libraries/FloatingActionButton/project.properties
Normal file
16
libraries/FloatingActionButton/project.properties
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-19
|
||||
android.library=true
|
14
libraries/FloatingActionButton/res/values/attrs.xml
Normal file
14
libraries/FloatingActionButton/res/values/attrs.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="FloatingActionButton">
|
||||
<attr name="drawable" format="integer"/>
|
||||
<attr name="color" format="color"/>
|
||||
<attr name="shadowRadius" format="float"/>
|
||||
<attr name="shadowDx" format="float"/>
|
||||
<attr name="shadowDy" format="float"/>
|
||||
<attr name="shadowColor" format="integer"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.AbsListView;
|
||||
|
||||
class DirectionScrollListener implements AbsListView.OnScrollListener {
|
||||
|
||||
private static final int DIRECTION_CHANGE_THRESHOLD = 1;
|
||||
private final FloatingActionButton mFloatingActionButton;
|
||||
private int mPrevPosition;
|
||||
private int mPrevTop;
|
||||
private boolean mUpdated;
|
||||
|
||||
DirectionScrollListener(FloatingActionButton floatingActionButton) {
|
||||
mFloatingActionButton = floatingActionButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
|
||||
final View topChild = view.getChildAt(0);
|
||||
int firstViewTop = 0;
|
||||
if (topChild != null) {
|
||||
firstViewTop = topChild.getTop();
|
||||
}
|
||||
boolean goingDown;
|
||||
boolean changed = true;
|
||||
if (mPrevPosition == firstVisibleItem) {
|
||||
final int topDelta = mPrevTop - firstViewTop;
|
||||
goingDown = firstViewTop < mPrevTop;
|
||||
changed = Math.abs(topDelta) > DIRECTION_CHANGE_THRESHOLD;
|
||||
} else {
|
||||
goingDown = firstVisibleItem > mPrevPosition;
|
||||
}
|
||||
if (changed && mUpdated) {
|
||||
if(goingDown)
|
||||
mFloatingActionButton.hideFab();
|
||||
else
|
||||
mFloatingActionButton.showFab();
|
||||
}
|
||||
mPrevPosition = firstVisibleItem;
|
||||
mPrevTop = firstViewTop;
|
||||
mUpdated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
|
||||
|
||||
package com.faizmalkani.floatingactionbutton;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.AbsListView;
|
||||
|
||||
public class FloatingActionButton extends View
|
||||
{
|
||||
Context _context;
|
||||
Paint mButtonPaint, mDrawablePaint;
|
||||
Bitmap mBitmap;
|
||||
int mScreenHeight;
|
||||
float currentY;
|
||||
boolean mHidden = false;
|
||||
|
||||
ObjectAnimator mShowAnimation;
|
||||
ObjectAnimator mHideAnimation;
|
||||
|
||||
public FloatingActionButton(Context context, AttributeSet attributeSet)
|
||||
{
|
||||
super(context, attributeSet);
|
||||
_context = context;
|
||||
init(Color.WHITE);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public FloatingActionButton(Context context)
|
||||
{
|
||||
super(context);
|
||||
_context = context;
|
||||
init(Color.WHITE);
|
||||
}
|
||||
|
||||
public void setColor(int fabColor)
|
||||
{
|
||||
init(fabColor);
|
||||
}
|
||||
|
||||
public void setDrawable(Drawable fabDrawable)
|
||||
{
|
||||
mBitmap = ((BitmapDrawable) fabDrawable).getBitmap();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
public void init(int fabColor)
|
||||
{
|
||||
setWillNotDraw(false);
|
||||
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
mButtonPaint.setColor(fabColor);
|
||||
mButtonPaint.setStyle(Paint.Style.FILL);
|
||||
mButtonPaint.setShadowLayer(10.0f, 0.0f, 3.5f, Color.argb(100, 0, 0, 0));
|
||||
mDrawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
invalidate();
|
||||
|
||||
WindowManager mWindowManager = (WindowManager) _context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = mWindowManager.getDefaultDisplay();
|
||||
Point size = new Point();
|
||||
display.getSize(size);
|
||||
mScreenHeight = size.y;
|
||||
|
||||
mShowAnimation = ObjectAnimator.ofFloat(this, "Y", currentY);
|
||||
mHideAnimation = ObjectAnimator.ofFloat(this, "Y", mScreenHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
setClickable(true);
|
||||
canvas.drawCircle(getWidth()/2, getHeight()/2,(float) (getWidth()/2.6), mButtonPaint);
|
||||
canvas.drawBitmap(mBitmap, (getWidth() - mBitmap.getWidth()) / 2, (getHeight() - mBitmap.getHeight()) / 2, mDrawablePaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
{
|
||||
if(event.getAction() == MotionEvent.ACTION_UP)
|
||||
{
|
||||
setAlpha(1.0f);
|
||||
}
|
||||
else if(event.getAction() == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
setAlpha(0.6f);
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public int dpToPx(int dp)
|
||||
{
|
||||
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
|
||||
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
|
||||
}
|
||||
|
||||
public void hideFab()
|
||||
{
|
||||
if(!mHidden && mShowAnimation != null && !mShowAnimation.isRunning())
|
||||
{
|
||||
currentY = getY();
|
||||
mHideAnimation = ObjectAnimator.ofFloat(this, "Y", mScreenHeight);
|
||||
mHideAnimation.setInterpolator(new AccelerateInterpolator());
|
||||
mHideAnimation.start();
|
||||
mHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void showFab()
|
||||
{
|
||||
if(mHidden && mHideAnimation != null && !mHideAnimation.isRunning())
|
||||
{
|
||||
mShowAnimation = ObjectAnimator.ofFloat(this, "Y", currentY);
|
||||
mShowAnimation.setInterpolator(new DecelerateInterpolator());
|
||||
mShowAnimation.start();
|
||||
mHidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHidden(){
|
||||
return mHidden;
|
||||
}
|
||||
|
||||
public void listenTo(AbsListView listView) {
|
||||
if (null != listView) {
|
||||
listView.setOnScrollListener(new DirectionScrollListener(this));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
33
libraries/RootCommands/.gitignore
vendored
Normal file
33
libraries/RootCommands/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
#Android specific
|
||||
bin
|
||||
gen
|
||||
obj
|
||||
libs/armeabi
|
||||
lint.xml
|
||||
local.properties
|
||||
release.properties
|
||||
ant.properties
|
||||
*.class
|
||||
*.apk
|
||||
|
||||
#Gradle
|
||||
.gradle
|
||||
build
|
||||
gradle.properties
|
||||
gradlew
|
||||
gradlew.bat
|
||||
gradle
|
||||
|
||||
#Maven
|
||||
target
|
||||
pom.xml.*
|
||||
|
||||
#Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings
|
||||
.metadata
|
||||
|
||||
#IntelliJ IDEA
|
||||
.idea
|
||||
*.iml
|
26
libraries/RootCommands/build.gradle
Normal file
26
libraries/RootCommands/build.gradle
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.+'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
dependencies {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 20
|
||||
buildToolsVersion "20.0.0"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 7
|
||||
targetSdkVersion 20
|
||||
}
|
||||
}
|
||||
|
15
libraries/RootCommands/src/main/AndroidManifest.xml
Normal file
15
libraries/RootCommands/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.sufficientlysecure.rootcommands"
|
||||
android:versionCode="3"
|
||||
android:versionName="1.2" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="7"
|
||||
android:targetSdkVersion="17" />
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
@@ -0,0 +1,43 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Mount {
|
||||
protected final File mDevice;
|
||||
protected final File mMountPoint;
|
||||
protected final String mType;
|
||||
protected final Set<String> mFlags;
|
||||
|
||||
Mount(File device, File path, String type, String flagsStr) {
|
||||
mDevice = device;
|
||||
mMountPoint = path;
|
||||
mType = type;
|
||||
mFlags = new HashSet<String>(Arrays.asList(flagsStr.split(",")));
|
||||
}
|
||||
|
||||
public File getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
public File getMountPoint() {
|
||||
return mMountPoint;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public Set<String> getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags);
|
||||
}
|
||||
}
|
@@ -0,0 +1,176 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.LineNumberReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
//no modifier, this means it is package-private. Only our internal classes can use this.
|
||||
class Remounter {
|
||||
|
||||
private Shell shell;
|
||||
|
||||
public Remounter(Shell shell) {
|
||||
super();
|
||||
this.shell = shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a path, which can contain the file name as well, and attempt to remount the
|
||||
* underlying partition.
|
||||
* <p/>
|
||||
* For example, passing in the following string:
|
||||
* "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately
|
||||
* being remounted. However, keep in mind that the longer the path you supply, the more work
|
||||
* this has to do, and the slower it will run.
|
||||
*
|
||||
* @param file
|
||||
* file path
|
||||
* @param mountType
|
||||
* mount type: pass in RO (Read only) or RW (Read Write)
|
||||
* @return a <code>boolean</code> which indicates whether or not the partition has been
|
||||
* remounted as specified.
|
||||
*/
|
||||
protected boolean remount(String file, String mountType) {
|
||||
|
||||
// if the path has a trailing slash get rid of it.
|
||||
if (file.endsWith("/") && !file.equals("/")) {
|
||||
file = file.substring(0, file.lastIndexOf("/"));
|
||||
}
|
||||
// Make sure that what we are trying to remount is in the mount list.
|
||||
boolean foundMount = false;
|
||||
while (!foundMount) {
|
||||
try {
|
||||
for (Mount mount : getMounts()) {
|
||||
Log.d(RootCommands.TAG, mount.getMountPoint().toString());
|
||||
|
||||
if (file.equals(mount.getMountPoint().toString())) {
|
||||
foundMount = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
return false;
|
||||
}
|
||||
if (!foundMount) {
|
||||
try {
|
||||
file = (new File(file).getParent()).toString();
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Mount mountPoint = findMountPointRecursive(file);
|
||||
|
||||
Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath()
|
||||
+ " as " + mountType.toLowerCase(Locale.US));
|
||||
final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US));
|
||||
|
||||
if (!isMountMode) {
|
||||
// grab an instance of the internal class
|
||||
try {
|
||||
SimpleCommand command = new SimpleCommand("busybox mount -o remount,"
|
||||
+ mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath()
|
||||
+ " " + mountPoint.getMountPoint().getAbsolutePath(),
|
||||
"toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount,"
|
||||
+ mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath(),
|
||||
"/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
|
||||
+ mountPoint.getDevice().getAbsolutePath() + " "
|
||||
+ mountPoint.getMountPoint().getAbsolutePath());
|
||||
|
||||
// execute on shell
|
||||
shell.add(command).waitForFinish();
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
mountPoint = findMountPointRecursive(file);
|
||||
}
|
||||
|
||||
if (mountPoint != null) {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US));
|
||||
if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
|
||||
return true;
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
|
||||
}
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "mountPoint is null");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Mount findMountPointRecursive(String file) {
|
||||
try {
|
||||
ArrayList<Mount> mounts = getMounts();
|
||||
for (File path = new File(file); path != null;) {
|
||||
for (Mount mount : mounts) {
|
||||
if (mount.getMountPoint().equals(path)) {
|
||||
return mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Exception", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return an ArrayList of the class Mount. The class mount contains the following
|
||||
* property's: device mountPoint type flags
|
||||
* <p/>
|
||||
* These will provide you with any information you need to work with the mount points.
|
||||
*
|
||||
* @return <code>ArrayList<Mount></code> an ArrayList of the class Mount.
|
||||
* @throws Exception
|
||||
* if we cannot return the mount points.
|
||||
*/
|
||||
protected static ArrayList<Mount> getMounts() throws Exception {
|
||||
|
||||
final String tempFile = "/data/local/RootToolsMounts";
|
||||
|
||||
// copy /proc/mounts to tempfile. Directly reading it does not work on 4.3
|
||||
Shell shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
tb.copyFile("/proc/mounts", tempFile, false, false);
|
||||
tb.setFilePermissions(tempFile, "777");
|
||||
shell.close();
|
||||
|
||||
LineNumberReader lnr = null;
|
||||
lnr = new LineNumberReader(new FileReader(tempFile));
|
||||
String line;
|
||||
ArrayList<Mount> mounts = new ArrayList<Mount>();
|
||||
while ((line = lnr.readLine()) != null) {
|
||||
|
||||
Log.d(RootCommands.TAG, line);
|
||||
|
||||
String[] fields = line.split(" ");
|
||||
mounts.add(new Mount(new File(fields[0]), // device
|
||||
new File(fields[1]), // mountPoint
|
||||
fields[2], // fstype
|
||||
fields[3] // flags
|
||||
));
|
||||
}
|
||||
lnr.close();
|
||||
|
||||
return mounts;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
public class RootCommands {
|
||||
public static boolean DEBUG = false;
|
||||
public static int DEFAULT_TIMEOUT = 10000;
|
||||
|
||||
public static final String TAG = "RootCommands";
|
||||
|
||||
/**
|
||||
* General method to check if user has su binary and accepts root access for this program!
|
||||
*
|
||||
* @return true if everything worked
|
||||
*/
|
||||
public static boolean rootAccessGiven() {
|
||||
boolean rootAccess = false;
|
||||
|
||||
try {
|
||||
Shell rootShell = Shell.startRootShell();
|
||||
|
||||
Toolbox tb = new Toolbox(rootShell);
|
||||
if (tb.isRootAccessGiven()) {
|
||||
rootAccess = true;
|
||||
}
|
||||
|
||||
rootShell.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Problem while checking for root access!", e);
|
||||
}
|
||||
|
||||
return rootAccess;
|
||||
}
|
||||
}
|
@@ -0,0 +1,331 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.Command;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException;
|
||||
import org.sufficientlysecure.rootcommands.util.Utils;
|
||||
|
||||
public class Shell implements Closeable {
|
||||
private final Process shellProcess;
|
||||
private final BufferedReader stdOutErr;
|
||||
private final DataOutputStream outputStream;
|
||||
private final List<Command> commands = new ArrayList<Command>();
|
||||
private boolean close = false;
|
||||
|
||||
private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH");
|
||||
private static final String token = "F*D^W@#FGF";
|
||||
|
||||
/**
|
||||
* Start root shell
|
||||
*
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startRootShell(ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException, RootAccessDeniedException {
|
||||
Log.d(RootCommands.TAG, "Starting Root Shell!");
|
||||
|
||||
// On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su
|
||||
// We need to pass LD_LIBRARY_PATH over su for some commands to work correctly.
|
||||
if (customEnv == null) {
|
||||
customEnv = new ArrayList<String>();
|
||||
}
|
||||
customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH);
|
||||
|
||||
Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start root shell without custom environment and base directory
|
||||
*
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startRootShell() throws IOException, RootAccessDeniedException {
|
||||
return startRootShell(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start default sh shell
|
||||
*
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startShell(ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException {
|
||||
Log.d(RootCommands.TAG, "Starting Shell!");
|
||||
Shell shell = new Shell("sh", customEnv, baseDirectory);
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start default sh shell without custom environment and base directory
|
||||
*
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startShell() throws IOException {
|
||||
return startShell(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start custom shell defined by shellPath
|
||||
*
|
||||
* @param shellPath
|
||||
* @param customEnv
|
||||
* @param baseDirectory
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startCustomShell(String shellPath, ArrayList<String> customEnv,
|
||||
String baseDirectory) throws IOException {
|
||||
Log.d(RootCommands.TAG, "Starting Custom Shell!");
|
||||
Shell shell = new Shell(shellPath, customEnv, baseDirectory);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start custom shell without custom environment and base directory
|
||||
*
|
||||
* @param shellPath
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public static Shell startCustomShell(String shellPath) throws IOException {
|
||||
return startCustomShell(shellPath, null, null);
|
||||
}
|
||||
|
||||
private Shell(String shell, ArrayList<String> customEnv, String baseDirectory)
|
||||
throws IOException, RootAccessDeniedException {
|
||||
Log.d(RootCommands.TAG, "Starting shell: " + shell);
|
||||
|
||||
// start shell process!
|
||||
shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory);
|
||||
|
||||
// StdErr is redirected to StdOut, defined in Command.getCommand()
|
||||
stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream()));
|
||||
outputStream = new DataOutputStream(shellProcess.getOutputStream());
|
||||
|
||||
outputStream.write("echo Started\n".getBytes());
|
||||
outputStream.flush();
|
||||
|
||||
while (true) {
|
||||
String line = stdOutErr.readLine();
|
||||
if (line == null)
|
||||
throw new RootAccessDeniedException(
|
||||
"stdout line is null! Access was denied or this executeable is not a shell!");
|
||||
if ("".equals(line))
|
||||
continue;
|
||||
if ("Started".equals(line))
|
||||
break;
|
||||
|
||||
destroyShellProcess();
|
||||
throw new IOException("Unable to start shell, unexpected output \"" + line + "\"");
|
||||
}
|
||||
|
||||
new Thread(inputRunnable, "Shell Input").start();
|
||||
new Thread(outputRunnable, "Shell Output").start();
|
||||
}
|
||||
|
||||
private Runnable inputRunnable = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
writeCommands();
|
||||
} catch (IOException e) {
|
||||
Log.e(RootCommands.TAG, "IO Exception", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable outputRunnable = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
readOutput();
|
||||
} catch (IOException e) {
|
||||
Log.e(RootCommands.TAG, "IOException", e);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "InterruptedException", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy shell process considering that the process could already be terminated
|
||||
*/
|
||||
private void destroyShellProcess() {
|
||||
try {
|
||||
// Yes, this really is the way to check if the process is
|
||||
// still running.
|
||||
shellProcess.exitValue();
|
||||
} catch (IllegalThreadStateException e) {
|
||||
// Only call destroy() if the process is still running;
|
||||
// Calling it for a terminated process will not crash, but
|
||||
// (starting with at least ICS/4.0) spam the log with INFO
|
||||
// messages ala "Failed to destroy process" and "kill
|
||||
// failed: ESRCH (No such process)".
|
||||
shellProcess.destroy();
|
||||
}
|
||||
|
||||
Log.d(RootCommands.TAG, "Shell destroyed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes queued commands one after another into the opened shell. After an execution a token is
|
||||
* written to seperate command output on read
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void writeCommands() throws IOException {
|
||||
try {
|
||||
int commandIndex = 0;
|
||||
while (true) {
|
||||
DataOutputStream out;
|
||||
synchronized (commands) {
|
||||
while (!close && commandIndex >= commands.size()) {
|
||||
commands.wait();
|
||||
}
|
||||
out = this.outputStream;
|
||||
}
|
||||
if (commandIndex < commands.size()) {
|
||||
Command next = commands.get(commandIndex);
|
||||
next.writeCommand(out);
|
||||
String line = "\necho " + token + " " + commandIndex + " $?\n";
|
||||
out.write(line.getBytes());
|
||||
out.flush();
|
||||
commandIndex++;
|
||||
} else if (close) {
|
||||
out.write("\nexit 0\n".getBytes());
|
||||
out.flush();
|
||||
out.close();
|
||||
Log.d(RootCommands.TAG, "Closing shell");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "interrupted while writing command", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads output line by line, seperated by token written after every command
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private void readOutput() throws IOException, InterruptedException {
|
||||
Command command = null;
|
||||
|
||||
// index of current command
|
||||
int commandIndex = 0;
|
||||
while (true) {
|
||||
String lineStdOut = stdOutErr.readLine();
|
||||
|
||||
// terminate on EOF
|
||||
if (lineStdOut == null)
|
||||
break;
|
||||
|
||||
if (command == null) {
|
||||
|
||||
// break on close after last command
|
||||
if (commandIndex >= commands.size()) {
|
||||
if (close)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// get current command
|
||||
command = commands.get(commandIndex);
|
||||
}
|
||||
|
||||
int pos = lineStdOut.indexOf(token);
|
||||
if (pos > 0) {
|
||||
command.processOutput(lineStdOut.substring(0, pos));
|
||||
}
|
||||
if (pos >= 0) {
|
||||
lineStdOut = lineStdOut.substring(pos);
|
||||
String fields[] = lineStdOut.split(" ");
|
||||
int id = Integer.parseInt(fields[1]);
|
||||
if (id == commandIndex) {
|
||||
command.setExitCode(Integer.parseInt(fields[2]));
|
||||
|
||||
// go to next command
|
||||
commandIndex++;
|
||||
command = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
command.processOutput(lineStdOut);
|
||||
}
|
||||
Log.d(RootCommands.TAG, "Read all output");
|
||||
shellProcess.waitFor();
|
||||
destroyShellProcess();
|
||||
|
||||
while (commandIndex < commands.size()) {
|
||||
if (command == null) {
|
||||
command = commands.get(commandIndex);
|
||||
}
|
||||
command.terminated("Unexpected Termination!");
|
||||
commandIndex++;
|
||||
command = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add command to shell queue
|
||||
*
|
||||
* @param command
|
||||
* @return
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public Command add(Command command) throws IOException {
|
||||
if (close)
|
||||
throw new IOException("Unable to add commands to a closed shell");
|
||||
synchronized (commands) {
|
||||
commands.add(command);
|
||||
// set shell on the command object, to know where the command is running on
|
||||
command.addedToShell(this, (commands.size() - 1));
|
||||
commands.notifyAll();
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close shell
|
||||
*
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (commands) {
|
||||
this.close = true;
|
||||
commands.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of queued commands
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getCommandsSize() {
|
||||
return commands.size();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.location.LocationManager;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
/**
|
||||
* This methods work when the apk is installed as a system app (under /system/app)
|
||||
*/
|
||||
public class SystemCommands {
|
||||
Context context;
|
||||
|
||||
public SystemCommands(Context context) {
|
||||
super();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GPS status
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean getGPS() {
|
||||
return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
|
||||
.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable GPS
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public void setGPS(boolean value) {
|
||||
ContentResolver localContentResolver = context.getContentResolver();
|
||||
Settings.Secure.setLocationProviderEnabled(localContentResolver,
|
||||
LocationManager.GPS_PROVIDER, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Not ready yet
|
||||
*/
|
||||
@TargetApi(8)
|
||||
public void reboot() {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
pm.reboot("recovery");
|
||||
pm.reboot(null);
|
||||
|
||||
// not working:
|
||||
// reboot(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot
|
||||
* system call. Should not return.
|
||||
*
|
||||
* Taken from com.android.server.PowerManagerService.reboot
|
||||
*/
|
||||
// public void reboot(String reason) {
|
||||
//
|
||||
// // final String finalReason = reason;
|
||||
// Runnable runnable = new Runnable() {
|
||||
// public void run() {
|
||||
// synchronized (this) {
|
||||
// // ShutdownThread.reboot(mContext, finalReason, false);
|
||||
// try {
|
||||
// Class<?> clazz = Class.forName("com.android.internal.app.ShutdownThread");
|
||||
//
|
||||
// // if (mReboot) {
|
||||
// Method method = clazz.getMethod("reboot", Context.class, String.class,
|
||||
// Boolean.TYPE);
|
||||
// method.invoke(null, context, null, false);
|
||||
//
|
||||
// // if (mReboot) {
|
||||
// // Method method = clazz.getMethod("reboot", Context.class, String.class,
|
||||
// // Boolean.TYPE);
|
||||
// // method.invoke(null, mContext, mReason, mConfirm);
|
||||
// // } else {
|
||||
// // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE);
|
||||
// // method.invoke(null, mContext, mConfirm);
|
||||
// // }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// };
|
||||
// // ShutdownThread must run on a looper capable of displaying the UI.
|
||||
// mHandler.post(runnable);
|
||||
//
|
||||
// // PowerManager.reboot() is documented not to return so just wait for the inevitable.
|
||||
// // synchronized (runnable) {
|
||||
// // while (true) {
|
||||
// // try {
|
||||
// // runnable.wait();
|
||||
// // } catch (InterruptedException e) {
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
||||
}
|
@@ -0,0 +1,809 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.command.ExecutableCommand;
|
||||
import org.sufficientlysecure.rootcommands.command.Command;
|
||||
import org.sufficientlysecure.rootcommands.command.SimpleCommand;
|
||||
import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
import android.os.StatFs;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/**
|
||||
* All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, but
|
||||
* normally shipped on every Android OS. You can find toolbox commands on
|
||||
* https://github.com/CyanogenMod/android_system_core/tree/ics/toolbox
|
||||
*
|
||||
* This means that these commands are designed to work on every Android OS, with a _working_ toolbox
|
||||
* binary on it. They don't require busybox!
|
||||
*
|
||||
*/
|
||||
public class Toolbox {
|
||||
private Shell shell;
|
||||
|
||||
/**
|
||||
* All methods in this class are working with Androids toolbox. Toolbox is similar to busybox,
|
||||
* but normally shipped on every Android OS.
|
||||
*
|
||||
* @param shell
|
||||
* where to execute commands on
|
||||
*/
|
||||
public Toolbox(Shell shell) {
|
||||
super();
|
||||
this.shell = shell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user accepted root access
|
||||
*
|
||||
* (commands: id)
|
||||
*
|
||||
* @return true if user has given root access
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public boolean isRootAccessGiven() throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
SimpleCommand idCommand = new SimpleCommand("id");
|
||||
shell.add(idCommand).waitForFinish();
|
||||
|
||||
if (idCommand.getOutput().contains("uid=0")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This command class gets all pids to a given process name
|
||||
*/
|
||||
private class PsCommand extends Command {
|
||||
private String processName;
|
||||
private ArrayList<String> pids;
|
||||
private String psRegex;
|
||||
private Pattern psPattern;
|
||||
|
||||
public PsCommand(String processName) {
|
||||
super("ps");
|
||||
this.processName = processName;
|
||||
pids = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* regex to get pid out of ps line, example:
|
||||
*
|
||||
* <pre>
|
||||
* root 24736 1 12140 584 ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
|
||||
* ^\\S \\s ([0-9]+) .* processName $
|
||||
* </pre>
|
||||
*/
|
||||
psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$";
|
||||
psPattern = Pattern.compile(psRegex);
|
||||
}
|
||||
|
||||
public ArrayList<String> getPids() {
|
||||
return pids;
|
||||
}
|
||||
|
||||
public String getPidsString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : pids) {
|
||||
sb.append(s);
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
// general check if line contains processName
|
||||
if (line.contains(processName)) {
|
||||
Matcher psMatcher = psPattern.matcher(line);
|
||||
|
||||
// try to match line exactly
|
||||
try {
|
||||
if (psMatcher.find()) {
|
||||
String pid = psMatcher.group(1);
|
||||
// add to pids list
|
||||
pids.add(pid);
|
||||
Log.d(RootCommands.TAG, "Found pid: " + pid);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "Matching in ps command failed!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Error with regex!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to kill a running process
|
||||
*
|
||||
* (commands: ps, kill)
|
||||
*
|
||||
* @param processName
|
||||
* name of process to kill
|
||||
* @return <code>true</code> if process was found and killed successfully
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public boolean killAll(String processName) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Killing process " + processName);
|
||||
|
||||
PsCommand psCommand = new PsCommand(processName);
|
||||
shell.add(psCommand).waitForFinish();
|
||||
|
||||
// kill processes
|
||||
if (!psCommand.getPids().isEmpty()) {
|
||||
// example: kill -9 1234 1222 5343
|
||||
SimpleCommand killCommand = new SimpleCommand("kill -9 "
|
||||
+ psCommand.getPidsString());
|
||||
shell.add(killCommand).waitForFinish();
|
||||
|
||||
if (killCommand.getExitCode() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "No pid found! Nothing was killed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill a running executable
|
||||
*
|
||||
* See README for more information how to use your own executables!
|
||||
*
|
||||
* @param executableName
|
||||
* @return
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public boolean killAllExecutable(String executableName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
return killAll(ExecutableCommand.EXECUTABLE_PREFIX + executableName + ExecutableCommand.EXECUTABLE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to to check if a process is running
|
||||
*
|
||||
* @param processName
|
||||
* name of process to check
|
||||
* @return <code>true</code> if process was found
|
||||
* @throws java.io.IOException
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* (Could not determine if the process is running)
|
||||
*/
|
||||
public boolean isProcessRunning(String processName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
PsCommand psCommand = new PsCommand(processName);
|
||||
shell.add(psCommand).waitForFinish();
|
||||
|
||||
// if pids are available process is running!
|
||||
if (!psCommand.getPids().isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if binary is running
|
||||
*
|
||||
* @param binaryName
|
||||
* @return
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public boolean isBinaryRunning(String binaryName) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName
|
||||
+ ExecutableCommand.EXECUTABLE_SUFFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ls command to get permissions or symlinks
|
||||
*/
|
||||
private class LsCommand extends Command {
|
||||
private String fileName;
|
||||
private String permissionRegex;
|
||||
private Pattern permissionPattern;
|
||||
private String symlinkRegex;
|
||||
private Pattern symlinkPattern;
|
||||
|
||||
private String symlink;
|
||||
private String permissions;
|
||||
|
||||
public String getSymlink() {
|
||||
return symlink;
|
||||
}
|
||||
|
||||
public String getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public LsCommand(String file) {
|
||||
super("ls -l " + file);
|
||||
|
||||
// get only filename:
|
||||
this.fileName = (new File(file)).getName();
|
||||
Log.d(RootCommands.TAG, "fileName: " + fileName);
|
||||
|
||||
/**
|
||||
* regex to get pid out of ps line, example:
|
||||
*
|
||||
* <pre>
|
||||
* with busybox:
|
||||
* lrwxrwxrwx 1 root root 15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0
|
||||
*
|
||||
* with toolbox:
|
||||
* lrwxrwxrwx root root 15 Aug 13 12:14 stdin -> /proc/self/fd/0
|
||||
*
|
||||
* Regex:
|
||||
* ^.*?(\\S{10}) .* $
|
||||
* </pre>
|
||||
*/
|
||||
permissionRegex = "^.*?(\\S{10}).*$";
|
||||
permissionPattern = Pattern.compile(permissionRegex);
|
||||
|
||||
/**
|
||||
* regex to get symlink
|
||||
*
|
||||
* <pre>
|
||||
* -> /proc/self/fd/0
|
||||
* ^.*?\\-\\> \\s+ (.*) $
|
||||
* </pre>
|
||||
*/
|
||||
symlinkRegex = "^.*?\\-\\>\\s+(.*)$";
|
||||
symlinkPattern = Pattern.compile(symlinkRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts permission string from ls command to numerical value. Example: -rwxrwxrwx gets
|
||||
* to 777
|
||||
*
|
||||
* @param permissions
|
||||
* @return
|
||||
*/
|
||||
private String convertPermissions(String permissions) {
|
||||
int owner = getGroupPermission(permissions.substring(1, 4));
|
||||
int group = getGroupPermission(permissions.substring(4, 7));
|
||||
int world = getGroupPermission(permissions.substring(7, 10));
|
||||
|
||||
return "" + owner + group + world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates permission for one group
|
||||
*
|
||||
* @param permission
|
||||
* @return value of permission string
|
||||
*/
|
||||
private int getGroupPermission(String permission) {
|
||||
int value = 0;
|
||||
|
||||
if (permission.charAt(0) == 'r') {
|
||||
value += 4;
|
||||
}
|
||||
if (permission.charAt(1) == 'w') {
|
||||
value += 2;
|
||||
}
|
||||
if (permission.charAt(2) == 'x') {
|
||||
value += 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
// general check if line contains file
|
||||
if (line.contains(fileName)) {
|
||||
|
||||
// try to match line exactly
|
||||
try {
|
||||
Matcher permissionMatcher = permissionPattern.matcher(line);
|
||||
if (permissionMatcher.find()) {
|
||||
permissions = convertPermissions(permissionMatcher.group(1));
|
||||
|
||||
Log.d(RootCommands.TAG, "Found permissions: " + permissions);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "Permissions were not found in ls command!");
|
||||
}
|
||||
|
||||
// try to parse for symlink
|
||||
Matcher symlinkMatcher = symlinkPattern.matcher(line);
|
||||
if (symlinkMatcher.find()) {
|
||||
/*
|
||||
* TODO: If symlink points to a file in the same directory the path is not
|
||||
* absolute!!!
|
||||
*/
|
||||
symlink = symlinkMatcher.group(1);
|
||||
Log.d(RootCommands.TAG, "Symlink found: " + symlink);
|
||||
} else {
|
||||
Log.d(RootCommands.TAG, "No symlink found!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(RootCommands.TAG, "Error with regex!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file
|
||||
* String that represent the file, including the full path to the file and its name.
|
||||
* @param followSymlinks
|
||||
* @return File permissions as String, for example: 777, returns null on error
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*
|
||||
*/
|
||||
public String getFilePermissions(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Checking permissions for " + file);
|
||||
|
||||
String permissions = null;
|
||||
|
||||
if (fileExists(file)) {
|
||||
Log.d(RootCommands.TAG, file + " was found.");
|
||||
|
||||
LsCommand lsCommand = new LsCommand(file);
|
||||
shell.add(lsCommand).waitForFinish();
|
||||
|
||||
permissions = lsCommand.getPermissions();
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets permission of file
|
||||
*
|
||||
* @param file
|
||||
* absolute path to file
|
||||
* @param permissions
|
||||
* String like 777
|
||||
* @return true if command worked
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public boolean setFilePermissions(String file, String permissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions);
|
||||
|
||||
SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file);
|
||||
shell.add(chmodCommand).waitForFinish();
|
||||
|
||||
if (chmodCommand.getExitCode() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will return a String that represent the symlink for a specified file.
|
||||
*
|
||||
* @param file
|
||||
* The path to the file to get the Symlink for. (must have absolute path)
|
||||
*
|
||||
* @return A String that represent the symlink for a specified file or null if no symlink
|
||||
* exists.
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public String getSymlink(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
Log.d(RootCommands.TAG, "Find symlink for " + file);
|
||||
|
||||
String symlink = null;
|
||||
|
||||
LsCommand lsCommand = new LsCommand(file);
|
||||
shell.add(lsCommand).waitForFinish();
|
||||
|
||||
symlink = lsCommand.getSymlink();
|
||||
|
||||
return symlink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copys a file to a destination. Because cp is not available on all android devices, we use dd
|
||||
* or cat.
|
||||
*
|
||||
* @param source
|
||||
* example: /data/data/org.adaway/files/hosts
|
||||
* @param destination
|
||||
* example: /system/etc/hosts
|
||||
* @param remountAsRw
|
||||
* remounts the destination as read/write before writing to it
|
||||
* @param preserveFileAttributes
|
||||
* tries to copy file attributes from source to destination, if only cat is available
|
||||
* only permissions are preserved
|
||||
* @return true if it was successfully copied
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
*/
|
||||
public boolean copyFile(String source, String destination, boolean remountAsRw,
|
||||
boolean preservePermissions) throws BrokenBusyboxException, IOException,
|
||||
TimeoutException {
|
||||
|
||||
/*
|
||||
* dd can only copy files, but we can not check if the source is a file without invoking
|
||||
* shell commands, because from Java we probably have no read access, thus we only check if
|
||||
* they are ending with trailing slashes
|
||||
*/
|
||||
if (source.endsWith("/") || destination.endsWith("/")) {
|
||||
throw new FileNotFoundException("dd can only copy files!");
|
||||
}
|
||||
|
||||
// remount destination as read/write before copying to it
|
||||
if (remountAsRw) {
|
||||
if (!remount(destination, "RW")) {
|
||||
Log.d(RootCommands.TAG,
|
||||
"Remounting failed! There is probably no need to remount this partition!");
|
||||
}
|
||||
}
|
||||
|
||||
// get permissions of source before overwriting
|
||||
String permissions = null;
|
||||
if (preservePermissions) {
|
||||
permissions = getFilePermissions(source);
|
||||
}
|
||||
|
||||
boolean commandSuccess = false;
|
||||
|
||||
SimpleCommand ddCommand = new SimpleCommand("dd if=" + source + " of="
|
||||
+ destination);
|
||||
shell.add(ddCommand).waitForFinish();
|
||||
|
||||
if (ddCommand.getExitCode() == 0) {
|
||||
commandSuccess = true;
|
||||
} else {
|
||||
// try cat if dd fails
|
||||
SimpleCommand catCommand = new SimpleCommand("cat " + source + " > "
|
||||
+ destination);
|
||||
shell.add(catCommand).waitForFinish();
|
||||
|
||||
if (catCommand.getExitCode() == 0) {
|
||||
commandSuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
// set back permissions from source to destination
|
||||
if (preservePermissions) {
|
||||
setFilePermissions(destination, permissions);
|
||||
}
|
||||
|
||||
// remount destination back to read only
|
||||
if (remountAsRw) {
|
||||
if (!remount(destination, "RO")) {
|
||||
Log.d(RootCommands.TAG,
|
||||
"Remounting failed! There is probably no need to remount this partition!");
|
||||
}
|
||||
}
|
||||
|
||||
return commandSuccess;
|
||||
}
|
||||
|
||||
public static final int REBOOT_HOTREBOOT = 1;
|
||||
public static final int REBOOT_REBOOT = 2;
|
||||
public static final int REBOOT_SHUTDOWN = 3;
|
||||
public static final int REBOOT_RECOVERY = 4;
|
||||
|
||||
/**
|
||||
* Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT,
|
||||
* REBOOT_SHUTDOWN, REBOOT_RECOVERY
|
||||
*
|
||||
* @param action
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void reboot(int action) throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
if (action == REBOOT_HOTREBOOT) {
|
||||
killAll("system_server");
|
||||
// or: killAll("zygote");
|
||||
} else {
|
||||
String command;
|
||||
switch (action) {
|
||||
case REBOOT_REBOOT:
|
||||
command = "reboot";
|
||||
break;
|
||||
case REBOOT_SHUTDOWN:
|
||||
command = "reboot -p";
|
||||
break;
|
||||
case REBOOT_RECOVERY:
|
||||
command = "reboot recovery";
|
||||
break;
|
||||
default:
|
||||
command = "reboot";
|
||||
break;
|
||||
}
|
||||
|
||||
SimpleCommand rebootCommand = new SimpleCommand(command);
|
||||
shell.add(rebootCommand).waitForFinish();
|
||||
|
||||
if (rebootCommand.getExitCode() == -1) {
|
||||
Log.e(RootCommands.TAG, "Reboot failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This command checks if a file exists
|
||||
*/
|
||||
private class FileExistsCommand extends Command {
|
||||
private String file;
|
||||
private boolean fileExists = false;
|
||||
|
||||
public FileExistsCommand(String file) {
|
||||
super("ls " + file);
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public boolean isFileExists() {
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
if (line.trim().equals(file)) {
|
||||
fileExists = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to check whether or not a file exists on the filesystem.
|
||||
*
|
||||
* @param file
|
||||
* String that represent the file, including the full path to the file and its name.
|
||||
*
|
||||
* @return a boolean that will indicate whether or not the file exists.
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*
|
||||
*/
|
||||
public boolean fileExists(String file) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
FileExistsCommand fileExistsCommand = new FileExistsCommand(file);
|
||||
shell.add(fileExistsCommand).waitForFinish();
|
||||
|
||||
if (fileExistsCommand.isFileExists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class WithPermissions {
|
||||
abstract void whileHavingPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute user defined Java code while having temporary permissions on a file
|
||||
*
|
||||
* @param file
|
||||
* @param withPermissions
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void withPermission(String file, String permission, WithPermissions withPermissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
String oldPermissions = getFilePermissions(file);
|
||||
|
||||
// set permissions (If set to 666, then Dalvik VM can also write to that file!)
|
||||
setFilePermissions(file, permission);
|
||||
|
||||
// execute user defined code
|
||||
withPermissions.whileHavingPermissions();
|
||||
|
||||
// set back to old permissions
|
||||
setFilePermissions(file, oldPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute user defined Java code while having temporary write permissions on a file using chmod
|
||||
* 666
|
||||
*
|
||||
* @param file
|
||||
* @param withWritePermissions
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void withWritePermissions(String file, WithPermissions withWritePermissions)
|
||||
throws BrokenBusyboxException, TimeoutException, IOException {
|
||||
withPermission(file, "666", withWritePermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets system clock using /dev/alarm
|
||||
*
|
||||
* @param millis
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void setSystemClock(final long millis) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
withWritePermissions("/dev/alarm", new WithPermissions() {
|
||||
|
||||
@Override
|
||||
void whileHavingPermissions() {
|
||||
SystemClock.setCurrentTimeMillis(millis);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust system clock by offset using /dev/alarm
|
||||
*
|
||||
* @param offset
|
||||
* @throws BrokenBusyboxException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
public void adjustSystemClock(final long offset) throws BrokenBusyboxException,
|
||||
TimeoutException, IOException {
|
||||
withWritePermissions("/dev/alarm", new WithPermissions() {
|
||||
|
||||
@Override
|
||||
void whileHavingPermissions() {
|
||||
SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + offset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will take a path, which can contain the file name as well, and attempt to remount the
|
||||
* underlying partition.
|
||||
*
|
||||
* For example, passing in the following string:
|
||||
* "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately
|
||||
* being remounted. However, keep in mind that the longer the path you supply, the more work
|
||||
* this has to do, and the slower it will run.
|
||||
*
|
||||
* @param file
|
||||
* file path
|
||||
* @param mountType
|
||||
* mount type: pass in RO (Read only) or RW (Read Write)
|
||||
* @return a <code>boolean</code> which indicates whether or not the partition has been
|
||||
* remounted as specified.
|
||||
*/
|
||||
public boolean remount(String file, String mountType) {
|
||||
// Recieved a request, get an instance of Remounter
|
||||
Remounter remounter = new Remounter(shell);
|
||||
// send the request
|
||||
return (remounter.remount(file, mountType));
|
||||
}
|
||||
|
||||
/**
|
||||
* This will tell you how the specified mount is mounted. rw, ro, etc...
|
||||
*
|
||||
* @param The
|
||||
* mount you want to check
|
||||
*
|
||||
* @return <code>String</code> What the mount is mounted as.
|
||||
* @throws Exception
|
||||
* if we cannot determine how the mount is mounted.
|
||||
*/
|
||||
public String getMountedAs(String path) throws Exception {
|
||||
ArrayList<Mount> mounts = Remounter.getMounts();
|
||||
if (mounts != null) {
|
||||
for (Mount mount : mounts) {
|
||||
if (path.contains(mount.getMountPoint().getAbsolutePath())) {
|
||||
Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]);
|
||||
return (String) mount.getFlags().toArray()[0];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
} else {
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is enough space on partition where target is located
|
||||
*
|
||||
* @param size
|
||||
* size of file to put on partition
|
||||
* @param target
|
||||
* path where to put the file
|
||||
*
|
||||
* @return true if it will fit on partition of target, false if it will not fit.
|
||||
*/
|
||||
public boolean hasEnoughSpaceOnPartition(String target, long size) {
|
||||
try {
|
||||
// new File(target).getFreeSpace() (API 9) is not working on data partition
|
||||
|
||||
// get directory without file
|
||||
String directory = new File(target).getParent().toString();
|
||||
|
||||
StatFs stat = new StatFs(directory);
|
||||
long blockSize = stat.getBlockSize();
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
long availableSpace = availableBlocks * blockSize;
|
||||
|
||||
Log.i(RootCommands.TAG, "Checking for enough space: Target: " + target
|
||||
+ ", directory: " + directory + " size: " + size + ", availableSpace: "
|
||||
+ availableSpace);
|
||||
|
||||
if (size < availableSpace) {
|
||||
return true;
|
||||
} else {
|
||||
Log.e(RootCommands.TAG, "Not enough space on partition!");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// if new StatFs(directory) fails catch IllegalArgumentException and just return true as
|
||||
// workaround
|
||||
Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Not tested!
|
||||
*
|
||||
* @param toggle
|
||||
* @throws java.io.IOException
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void toggleAdbDaemon(boolean toggle) throws BrokenBusyboxException, TimeoutException,
|
||||
IOException {
|
||||
SimpleCommand disableAdb = new SimpleCommand("setprop persist.service.adb.enable 0",
|
||||
"stop adbd");
|
||||
SimpleCommand enableAdb = new SimpleCommand("setprop persist.service.adb.enable 1",
|
||||
"stop adbd", "sleep 1", "start adbd");
|
||||
|
||||
if (toggle) {
|
||||
shell.add(enableAdb).waitForFinish();
|
||||
} else {
|
||||
shell.add(disableAdb).waitForFinish();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
|
||||
import org.sufficientlysecure.rootcommands.util.Log;
|
||||
|
||||
public abstract class Command {
|
||||
final String command[];
|
||||
boolean finished = false;
|
||||
boolean brokenBusyboxDetected = false;
|
||||
int exitCode;
|
||||
int id;
|
||||
int timeout = RootCommands.DEFAULT_TIMEOUT;
|
||||
Shell shell = null;
|
||||
|
||||
public Command(String... command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public Command(int timeout, String... command) {
|
||||
this.command = command;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called from Shell after adding it
|
||||
*
|
||||
* @param shell
|
||||
* @param id
|
||||
*/
|
||||
public void addedToShell(Shell shell, int id) {
|
||||
this.shell = shell;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets command string executed on the shell
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCommand() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < command.length; i++) {
|
||||
// redirect stderr to stdout
|
||||
sb.append(command[i] + " 2>&1");
|
||||
sb.append('\n');
|
||||
}
|
||||
Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void writeCommand(OutputStream out) throws IOException {
|
||||
out.write(getCommand().getBytes());
|
||||
}
|
||||
|
||||
public void processOutput(String line) {
|
||||
Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line);
|
||||
|
||||
/*
|
||||
* Try to detect broken toolbox/busybox binaries (see
|
||||
* https://code.google.com/p/busybox-android/issues/detail?id=1)
|
||||
*
|
||||
* It is giving "Value too large for defined data type" on certain file operations (e.g. ls
|
||||
* and chown) in certain directories (e.g. /data/data)
|
||||
*/
|
||||
if (line.contains("Value too large for defined data type")) {
|
||||
Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line);
|
||||
brokenBusyboxDetected = true;
|
||||
}
|
||||
|
||||
// now execute specific output parsing
|
||||
output(id, line);
|
||||
}
|
||||
|
||||
public abstract void output(int id, String line);
|
||||
|
||||
public void processAfterExecution(int exitCode) {
|
||||
Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode);
|
||||
|
||||
afterExecution(id, exitCode);
|
||||
}
|
||||
|
||||
public abstract void afterExecution(int id, int exitCode);
|
||||
|
||||
public void commandFinished(int id) {
|
||||
Log.d(RootCommands.TAG, "Command " + id + " finished.");
|
||||
}
|
||||
|
||||
public void setExitCode(int code) {
|
||||
synchronized (this) {
|
||||
exitCode = code;
|
||||
finished = true;
|
||||
commandFinished(id);
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the shell
|
||||
*
|
||||
* @param reason
|
||||
*/
|
||||
public void terminate(String reason) {
|
||||
try {
|
||||
shell.close();
|
||||
Log.d(RootCommands.TAG, "Terminating the shell.");
|
||||
terminated(reason);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void terminated(String reason) {
|
||||
setExitCode(-1);
|
||||
Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for this command to finish and forwards exitCode into afterExecution method
|
||||
*
|
||||
* @param timeout
|
||||
* @throws java.util.concurrent.TimeoutException
|
||||
* @throws BrokenBusyboxException
|
||||
*/
|
||||
public void waitForFinish() throws TimeoutException, BrokenBusyboxException {
|
||||
synchronized (this) {
|
||||
while (!finished) {
|
||||
try {
|
||||
this.wait(timeout);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e);
|
||||
}
|
||||
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
terminate("Timeout");
|
||||
throw new TimeoutException("Timeout has occurred.");
|
||||
}
|
||||
}
|
||||
|
||||
if (brokenBusyboxDetected) {
|
||||
throw new BrokenBusyboxException();
|
||||
}
|
||||
|
||||
processAfterExecution(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
public abstract class ExecutableCommand extends Command {
|
||||
public static final String EXECUTABLE_PREFIX = "lib";
|
||||
public static final String EXECUTABLE_SUFFIX = "_exec.so";
|
||||
|
||||
/**
|
||||
* This class provides a way to use your own binaries!
|
||||
*
|
||||
* Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the
|
||||
* architecture directories. Now they will be deployed by Android the same way libraries are
|
||||
* deployed!
|
||||
*
|
||||
* See README for more information how to use your own executables!
|
||||
*
|
||||
* @param context
|
||||
* @param executableName
|
||||
* @param parameters
|
||||
*/
|
||||
public ExecutableCommand(Context context, String executableName, String parameters) {
|
||||
super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName
|
||||
+ EXECUTABLE_SUFFIX + " " + parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full path to lib directory of app
|
||||
*
|
||||
* @return dir as String
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
private static String getLibDirectory(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 9) {
|
||||
return context.getApplicationInfo().nativeLibraryDir;
|
||||
} else {
|
||||
return context.getApplicationInfo().dataDir + File.separator + "lib";
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void output(int id, String line);
|
||||
|
||||
public abstract void afterExecution(int id, int exitCode);
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
public class SimpleCommand extends Command {
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public SimpleCommand(String... command) {
|
||||
super(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.command;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class SimpleExecutableCommand extends ExecutableCommand {
|
||||
private StringBuilder sb = new StringBuilder();
|
||||
|
||||
public SimpleExecutableCommand(Context context, String executableName, String parameters) {
|
||||
super(context, executableName, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void output(int id, String line) {
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterExecution(int id, int exitCode) {
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BrokenBusyboxException extends IOException {
|
||||
private static final long serialVersionUID = 8337358201589488409L;
|
||||
|
||||
public BrokenBusyboxException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BrokenBusyboxException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
|
||||
/**
|
||||
* Wraps Android Logging to enable or disable debug output using Constants
|
||||
*
|
||||
*/
|
||||
public final class Log {
|
||||
|
||||
public static void v(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.v(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void v(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.v(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.d(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.d(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.i(tag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void i(String tag, String msg, Throwable tr) {
|
||||
if (RootCommands.DEBUG) {
|
||||
android.util.Log.i(tag, msg, tr);
|
||||
}
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg) {
|
||||
android.util.Log.w(tag, msg);
|
||||
}
|
||||
|
||||
public static void w(String tag, String msg, Throwable tr) {
|
||||
android.util.Log.w(tag, msg, tr);
|
||||
}
|
||||
|
||||
public static void w(String tag, Throwable tr) {
|
||||
android.util.Log.w(tag, tr);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
android.util.Log.e(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg, Throwable tr) {
|
||||
android.util.Log.e(tag, msg, tr);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RootAccessDeniedException extends IOException {
|
||||
private static final long serialVersionUID = 9088998884166225540L;
|
||||
|
||||
public RootAccessDeniedException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public RootAccessDeniedException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
public class UnsupportedArchitectureException extends Exception {
|
||||
private static final long serialVersionUID = 7826528799780001655L;
|
||||
|
||||
public UnsupportedArchitectureException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UnsupportedArchitectureException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
|
||||
|
||||
package org.sufficientlysecure.rootcommands.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.RootCommands;
|
||||
|
||||
public class Utils {
|
||||
/*
|
||||
* The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow
|
||||
* apps to use it (su app_29 $ su su: uid 10029 not allowed to su).
|
||||
*
|
||||
* Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to
|
||||
* /system/xbin/su.
|
||||
*
|
||||
* The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet.
|
||||
*/
|
||||
static final String[] BinaryPlaces = { "/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/",
|
||||
"/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/",
|
||||
"/data/local/" };
|
||||
|
||||
/**
|
||||
* Determine the path of the su executable.
|
||||
*
|
||||
* Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
|
||||
* agreed by Michael Elsdörfer
|
||||
*/
|
||||
public static String getSuPath() {
|
||||
for (String p : BinaryPlaces) {
|
||||
File su = new File(p + "su");
|
||||
if (su.exists()) {
|
||||
Log.d(RootCommands.TAG, "su found at: " + p);
|
||||
return su.getAbsolutePath();
|
||||
} else {
|
||||
Log.v(RootCommands.TAG, "No su in: " + p);
|
||||
}
|
||||
}
|
||||
Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\".");
|
||||
return "su";
|
||||
}
|
||||
|
||||
/**
|
||||
* This code is adapted from java.lang.ProcessBuilder.start().
|
||||
*
|
||||
* The problem is that Android doesn't allow us to modify the map returned by
|
||||
* ProcessBuilder.environment(), even though the docstring indicates that it should. This is
|
||||
* because it simply returns the SystemEnvironment object that System.getenv() gives us. The
|
||||
* relevant portion in the source code is marked as "// android changed", so presumably it's not
|
||||
* the case in the original version of the Apache Harmony project.
|
||||
*
|
||||
* Note that simply passing the environment variables we want to Process.exec won't be good
|
||||
* enough, since that would override the environment we inherited completely.
|
||||
*
|
||||
* We needed to be able to set a CLASSPATH environment variable for our new process in order to
|
||||
* use the "app_process" command directly. Note: "app_process" takes arguments passed on to the
|
||||
* Dalvik VM as well; this might be an alternative way to set the class path.
|
||||
*
|
||||
* Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
|
||||
* agreed by Michael Elsdörfer
|
||||
*/
|
||||
public static Process runWithEnv(String command, ArrayList<String> customAddedEnv,
|
||||
String baseDirectory) throws IOException {
|
||||
|
||||
Map<String, String> environment = System.getenv();
|
||||
String[] envArray = new String[environment.size()
|
||||
+ (customAddedEnv != null ? customAddedEnv.size() : 0)];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> entry : environment.entrySet()) {
|
||||
envArray[i++] = entry.getKey() + "=" + entry.getValue();
|
||||
}
|
||||
if (customAddedEnv != null) {
|
||||
for (String entry : customAddedEnv) {
|
||||
envArray[i++] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
Process process;
|
||||
if (baseDirectory == null) {
|
||||
process = Runtime.getRuntime().exec(command, envArray, null);
|
||||
} else {
|
||||
process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory));
|
||||
}
|
||||
return process;
|
||||
}
|
||||
}
|
1
libraries/sharedCode/.gitignore
vendored
Normal file
1
libraries/sharedCode/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
61
libraries/sharedCode/build.gradle
Normal file
61
libraries/sharedCode/build.gradle
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'ExtraTranslation'
|
||||
}
|
||||
|
||||
compileSdkVersion 20
|
||||
buildToolsVersion "20.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "sharedcode.turboeditor"
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 20
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
runProguard false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':libraries:RootCommands')
|
||||
compile project(':libraries:FloatingActionButton')
|
||||
compile "com.android.support:support-v4:19.0.+"
|
||||
compile('de.greenrobot:eventbus:2.2.1') {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
compile 'com.github.gabrielemariotti.changeloglib:library:1.5.1'
|
||||
compile 'commons-io:commons-io:2.4'
|
||||
|
||||
}
|
BIN
libraries/sharedCode/libs/juniversalchardet-1.0.3.jar
Normal file
BIN
libraries/sharedCode/libs/juniversalchardet-1.0.3.jar
Normal file
Binary file not shown.
17
libraries/sharedCode/proguard-rules.pro
vendored
Normal file
17
libraries/sharedCode/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:/Users/Vlad/AppData/Local/Android/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
24
libraries/sharedCode/src/main/AndroidManifest.xml
Normal file
24
libraries/sharedCode/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
~ Copyright (C) 2014 Vlad Mihalachi
|
||||
~
|
||||
~ This file is part of Turbo Editor.
|
||||
~
|
||||
~ Turbo Editor is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Turbo Editor is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="sharedcode.turboeditor">
|
||||
|
||||
|
||||
</manifest>
|
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.activity;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.ActionBarDrawerToggle;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.fragment.ChangelogDialogFragment;
|
||||
import sharedcode.turboeditor.fragment.EditorFragment;
|
||||
import sharedcode.turboeditor.fragment.NoFileOpenedFragment;
|
||||
import sharedcode.turboeditor.preferences.SettingsFragment;
|
||||
import sharedcode.turboeditor.util.AppInfoHelper;
|
||||
import sharedcode.turboeditor.util.Constants;
|
||||
import sharedcode.turboeditor.util.ProCheckUtils;
|
||||
import sharedcode.turboeditor.views.CustomDrawerLayout;
|
||||
import sharedcode.turboeditor.util.EventBusEvents;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
import sharedcode.turboeditor.util.ThemeHelper;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.Toolbox;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public abstract class BaseHomeActivity extends Activity {
|
||||
|
||||
private static final int SELECT_FILE_CODE = 121;
|
||||
private EditText editor;
|
||||
|
||||
/*
|
||||
* This class provides a handy way to tie together the functionality of
|
||||
* {@link DrawerLayout} and the framework <code>ActionBar</code> to implement the recommended
|
||||
* design for navigation drawers.
|
||||
*/
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
/*
|
||||
* The Drawer Layout
|
||||
*/
|
||||
private CustomDrawerLayout mDrawerLayout;
|
||||
|
||||
//region Activity facts
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// set the windows background
|
||||
ThemeHelper.setWindowsBackground(this);
|
||||
// super!!
|
||||
super.onCreate(savedInstanceState);
|
||||
// setup the layout
|
||||
setContentView(R.layout.activity_home);
|
||||
// setup the navigation drawer
|
||||
setupNavigationDrawer();
|
||||
// Replace fragment
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_editor, new NoFileOpenedFragment())
|
||||
.commit();
|
||||
/* First Time we open this activity */
|
||||
if (savedInstanceState == null) {
|
||||
// Open
|
||||
mDrawerLayout.openDrawer(Gravity.START);
|
||||
// Set the default title
|
||||
getActionBar().setTitle(getString(R.string.nome_app_turbo_editor));
|
||||
}
|
||||
// parse the intent
|
||||
parseIntent(getIntent());
|
||||
// show a dialog with the changelog
|
||||
showChangeLog();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected final void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
mDrawerToggle.syncState();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// Register the Event Bus for events
|
||||
EventBus.getDefault().registerSticky(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
// Unregister the Event Bus
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
try {
|
||||
closeKeyBoard();
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (getFragmentManager().findFragmentById(R.id.fragment_editor) instanceof EditorFragment) {
|
||||
|
||||
// remove editor fragment
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_editor, new NoFileOpenedFragment())
|
||||
.commit();
|
||||
// Set the default title
|
||||
getActionBar().setTitle(getString(R.string.nome_app_turbo_editor));
|
||||
|
||||
EventBus.getDefault().post(new EventBusEvents.ClosedAFile());
|
||||
|
||||
mDrawerLayout.openDrawer(Gravity.START);
|
||||
mDrawerLayout.closeDrawer(Gravity.END);
|
||||
} else {
|
||||
displayInterstitial();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (editor == null)
|
||||
editor = (EditText) findViewById(R.id.editor);
|
||||
// this will happen on first key pressed on hard-keyboard only. Once myInputField
|
||||
// gets the focus again, it will automatically receive further key presses.
|
||||
|
||||
try{
|
||||
if (editor != null && !editor.hasFocus()) {
|
||||
editor.requestFocus();
|
||||
editor.onKeyDown(keyCode, event);
|
||||
}
|
||||
} catch (NullPointerException ex){
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
mDrawerToggle.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == RESULT_OK && requestCode == SELECT_FILE_CODE) {
|
||||
String path = data.getStringExtra("path");
|
||||
File file = new File(path);
|
||||
if (file.isFile() && file.exists()) {
|
||||
EventBus.getDefault().postSticky(new EventBusEvents.NewFileToOpen(new File(path)));
|
||||
} else if(file.isDirectory()) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
/* If we clicked on the Navigation Drawer Menu item */
|
||||
if (mDrawerToggle.onOptionsItemSelected(item)) {
|
||||
mDrawerLayout.closeDrawer(Gravity.RIGHT);
|
||||
return true;
|
||||
} else
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
parseIntent(intent);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Calls from the layout
|
||||
public void OpenFile(View view) {
|
||||
Intent subActivity = new Intent(BaseHomeActivity.this, SelectFileActivity.class);
|
||||
subActivity.putExtra("action", SelectFileActivity.Actions.SelectFile);
|
||||
Bundle scaleBundle = ActivityOptionsCompat.makeScaleUpAnimation(
|
||||
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
startActivityForResult(subActivity, SELECT_FILE_CODE, scaleBundle);
|
||||
else
|
||||
startActivityForResult(subActivity, SELECT_FILE_CODE);
|
||||
}
|
||||
|
||||
public void CreateFile(View view) {
|
||||
onEvent(new EventBusEvents.NewFileToOpen(""));
|
||||
onEvent(new EventBusEvents.AFileIsSelected("")); // simulate click on the list
|
||||
}
|
||||
|
||||
public void OpenInfo(View view) {
|
||||
Intent subActivity = new Intent(BaseHomeActivity.this, PreferenceAbout.class);
|
||||
Bundle scaleBundle = ActivityOptionsCompat.makeScaleUpAnimation(
|
||||
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
|
||||
startActivity(subActivity, scaleBundle);
|
||||
else
|
||||
startActivity(subActivity);
|
||||
}
|
||||
|
||||
public void OpenSettings(View view) {
|
||||
mDrawerLayout.closeDrawer(Gravity.START);
|
||||
mDrawerLayout.openDrawer(Gravity.END);
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Eventbus
|
||||
public void onEvent(final EventBusEvents.NewFileToOpen event) {
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
|
||||
File file;
|
||||
String message;
|
||||
String fileText;
|
||||
ProgressDialog progressDialog;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
// Close the drawer
|
||||
mDrawerLayout.closeDrawer(Gravity.START);
|
||||
file = event.getFile();
|
||||
message = "";
|
||||
progressDialog = new ProgressDialog(BaseHomeActivity.this);
|
||||
progressDialog.setMessage(getString(R.string.please_wait));
|
||||
progressDialog.show();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
boolean isRoot = false;
|
||||
|
||||
if (!file.exists()) {
|
||||
fileText = event.getFileText();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!file.canRead()) {
|
||||
Shell shell = null;
|
||||
shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
isRoot = tb.isRootAccessGiven();
|
||||
}
|
||||
|
||||
if (isRoot) {
|
||||
File tempFile = new File(getFilesDir(), "temp.root.file");
|
||||
if (!tempFile.exists())
|
||||
tempFile.createNewFile();
|
||||
Shell shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
tb.copyFile(event.getFile().getAbsolutePath(), tempFile.getAbsolutePath(), false, false);
|
||||
file = new File(tempFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
boolean autoencoding = PreferenceHelper.getAutoEncoding(BaseHomeActivity.this);
|
||||
|
||||
if (autoencoding) {
|
||||
|
||||
String encoding = sharedcode.turboeditor.util.FileUtils.getDetectedEncoding(file);
|
||||
if (!TextUtils.isEmpty(encoding)) {
|
||||
encoding = SettingsFragment.sCurrentEncoding;
|
||||
} else {
|
||||
SettingsFragment.sCurrentEncoding = encoding;
|
||||
}
|
||||
|
||||
fileText = org.apache.commons.io.FileUtils.readFileToString(file, encoding);
|
||||
} else {
|
||||
fileText = org.apache.commons.io.FileUtils.readFileToString(file, SettingsFragment.sCurrentEncoding);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
message = e.getMessage();
|
||||
fileText = "";
|
||||
}
|
||||
while (mDrawerLayout.isDrawerOpen(Gravity.START)) {
|
||||
try {
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
super.onPostExecute(result);
|
||||
progressDialog.hide();
|
||||
|
||||
if (!message.isEmpty()) {
|
||||
Toast.makeText(BaseHomeActivity.this, message, Toast.LENGTH_LONG).show();
|
||||
EventBus.getDefault().post(new EventBusEvents.CannotOpenAFile());
|
||||
} else {
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_editor, EditorFragment.newInstance(event.getFile().getAbsolutePath(), fileText))
|
||||
.commit();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}.execute();
|
||||
|
||||
}
|
||||
|
||||
public void onEvent(EventBusEvents.SavedAFile event) {
|
||||
try {
|
||||
closeKeyBoard();
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Get intent, action and MIME type
|
||||
final Intent intent = getIntent();
|
||||
final String action = intent.getAction();
|
||||
final String type = intent.getType();
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(action)
|
||||
|| Intent.ACTION_EDIT.equals(action)
|
||||
|| Intent.ACTION_PICK.equals(action)
|
||||
&& type != null) {
|
||||
//This Activity was called by startActivityForResult
|
||||
final Intent returnIntent = new Intent();
|
||||
setResult(Activity.RESULT_OK, returnIntent);
|
||||
// finish the activity
|
||||
finish();
|
||||
}
|
||||
if (!ProCheckUtils.isPro(getApplicationContext()))
|
||||
displayInterstitial();
|
||||
}
|
||||
|
||||
public void onEvent(EventBusEvents.AFileIsSelected event){
|
||||
String name = FilenameUtils.getName(event.getPath());
|
||||
if(name.isEmpty())
|
||||
getActionBar().setTitle(R.string.nome_app_turbo_editor);
|
||||
else
|
||||
getActionBar().setTitle(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a file can't be opened
|
||||
* Invoked by the EditorFragment
|
||||
*
|
||||
* @param event The event called
|
||||
*/
|
||||
public void onEvent(EventBusEvents.CannotOpenAFile event) {
|
||||
//
|
||||
mDrawerLayout.openDrawer(Gravity.LEFT);
|
||||
//
|
||||
getActionBar().setTitle(getString(R.string.nome_app_turbo_editor));
|
||||
// Replace fragment
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_editor, new NoFileOpenedFragment())
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void onEvent(EventBusEvents.APreferenceValueWasChanged event) {
|
||||
|
||||
if (event.getType() == EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE) {
|
||||
ThemeHelper.setWindowsBackground(this);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
// closes the soft keyboard
|
||||
private void closeKeyBoard() throws NullPointerException {
|
||||
// Central system API to the overall input method framework (IMF) architecture
|
||||
InputMethodManager inputManager =
|
||||
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
// Base interface for a remotable object
|
||||
IBinder windowToken = getCurrentFocus().getWindowToken();
|
||||
|
||||
// Hide type
|
||||
int hideType = InputMethodManager.HIDE_NOT_ALWAYS;
|
||||
|
||||
// Hide the KeyBoard
|
||||
inputManager.hideSoftInputFromWindow(windowToken, hideType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the navigation drawer
|
||||
*/
|
||||
private void setupNavigationDrawer() {
|
||||
mDrawerLayout = (CustomDrawerLayout) findViewById(R.id.drawer_layout);
|
||||
/* Action Bar */
|
||||
final ActionBar ab = getActionBar();
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
ab.setHomeButtonEnabled(true);
|
||||
/* Navigation drawer */
|
||||
mDrawerToggle =
|
||||
new ActionBarDrawerToggle(
|
||||
this,
|
||||
mDrawerLayout,
|
||||
R.drawable.ic_drawer,
|
||||
R.string.nome_app_turbo_editor,
|
||||
R.string.nome_app_turbo_editor) {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerClosed(View view) {
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
invalidateOptionsMenu();
|
||||
try {
|
||||
closeKeyBoard();
|
||||
} catch (NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
/* link the mDrawerToggle to the Drawer Layout */
|
||||
mDrawerLayout.setDrawerListener(mDrawerToggle);
|
||||
//mDrawerLayout.setFocusableInTouchMode(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog with the changelog
|
||||
*/
|
||||
private void showChangeLog() {
|
||||
final String currentVersion = AppInfoHelper.getCurrentVersion(this);
|
||||
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
final String lastVersion = preferences.getString("last_version", currentVersion);
|
||||
preferences.edit().putString("last_version", currentVersion).apply();
|
||||
if (!lastVersion.equals(currentVersion)) {
|
||||
ChangelogDialogFragment.showChangeLogDialog(getFragmentManager());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the intent
|
||||
*/
|
||||
private void parseIntent(Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
final String type = intent.getType();
|
||||
|
||||
if (Intent.ACTION_VIEW.equals(action)
|
||||
|| Intent.ACTION_EDIT.equals(action)
|
||||
|| Intent.ACTION_PICK.equals(action)
|
||||
&& type != null) {
|
||||
// Post event
|
||||
EventBus.getDefault().postSticky(new EventBusEvents.NewFileToOpen(new File(intent.getData().getPath())));
|
||||
}
|
||||
else if (Intent.ACTION_SEND.equals(action) && type != null) {
|
||||
if ("text/plain".equals(type)) {
|
||||
onEvent(new EventBusEvents.NewFileToOpen(intent.getStringExtra(Intent.EXTRA_TEXT)));
|
||||
onEvent(new EventBusEvents.AFileIsSelected("")); // simulate click on the list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void displayInterstitial();
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
|
||||
public class LicensesActivity extends Activity implements AdapterView.OnItemClickListener {
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
boolean light = PreferenceHelper.getLightTheme(this);
|
||||
if (light) {
|
||||
setTheme(R.style.AppTheme_Light);
|
||||
} else {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
}
|
||||
setContentView(R.layout.activity_licenses);
|
||||
ListView listView = (ListView) findViewById(android.R.id.list);
|
||||
listView.setOnItemClickListener(this);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.open_source_libs));
|
||||
listView.setAdapter(adapter);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
String openSourceLib = ((TextView) view.findViewById(android.R.id.text1)).getText().toString();
|
||||
Intent browserIntent = null;
|
||||
switch (openSourceLib) {
|
||||
case "ChangeLog Library":
|
||||
browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/gabrielemariotti/changeloglib?source=c#license"));
|
||||
break;
|
||||
case "EventBus":
|
||||
browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/greenrobot/EventBus?source=c#license"));
|
||||
break;
|
||||
case "commons-io":
|
||||
browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://commons.apache.org/proper/commons-io/"));
|
||||
break;
|
||||
case "RootCommands":
|
||||
browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/dschuermann/superuser-commands"));
|
||||
break;
|
||||
case "Floating Action Button":
|
||||
browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/makovkastar/FloatingActionButton"));
|
||||
break;
|
||||
}
|
||||
if (browserIntent != null) {
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.util.AppInfoHelper;
|
||||
import sharedcode.turboeditor.util.Constants;
|
||||
import sharedcode.turboeditor.util.ProCheckUtils;
|
||||
|
||||
public class PreferenceAbout extends Activity {
|
||||
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_about);
|
||||
|
||||
TextView changeLogText = (TextView) findViewById(R.id.changelog_text);
|
||||
TextView proVersionText = (TextView) findViewById(R.id.pro_version_text);
|
||||
|
||||
try {
|
||||
changeLogText.setText(String.format(getString(R.string.app_version), getPackageManager().getPackageInfo(getPackageName(), 0).versionName));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
proVersionText.setVisibility(ProCheckUtils.isPro(getBaseContext()) ? View.GONE : View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
//checkout.stop();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void OpenPlayStore(View view) {
|
||||
try {
|
||||
if (Constants.FOR_AMAZON) {
|
||||
String url = "amzn://apps/android?p=com.maskyn.fileeditor";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
} else {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pub:Maskyn"))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void GoToProVersion(View view) {
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditorpro"))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenGithub(View view) {
|
||||
String url = "https://github.com/vmihalachi/TurboEditor";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
public void SendFeedback(View view) {
|
||||
String url = "http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
public void SendMail(View view) {
|
||||
Intent i = new Intent(Intent.ACTION_SEND);
|
||||
i.setType("message/rfc822");
|
||||
i.putExtra(Intent.EXTRA_EMAIL, new String[]{"maskyngames@gmail.com"});
|
||||
i.putExtra(Intent.EXTRA_SUBJECT, AppInfoHelper.getApplicationName(getBaseContext()) + " " + AppInfoHelper.getCurrentVersion(getBaseContext()));
|
||||
i.putExtra(Intent.EXTRA_TEXT, "");
|
||||
try {
|
||||
startActivity(Intent.createChooser(i, getString(R.string.nome_app_turbo_editor)));
|
||||
} catch (android.content.ActivityNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenTranslatePage(View view) {
|
||||
String url = "http://crowdin.net/project/turbo-client";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
public void OpenGooglePlusCommunity(View view) {
|
||||
String url = "https://plus.google.com/u/0/communities/111974095419108178946";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
|
||||
/*void setupClickablePreferences() {
|
||||
final Preference email = findPreference("aboutactivity_authoremail"),
|
||||
changelog = findPreference("aboutactivity_changelog"),
|
||||
open_source_licenses = findPreference("aboutactivity_open_source_licenses"),
|
||||
market = findPreference("aboutactivity_authormarket");
|
||||
if (email != null) {
|
||||
email.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(final Preference preference) {
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (changelog != null) {
|
||||
changelog.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(final Preference preference) {
|
||||
ChangelogDialogFragment.showChangeLogDialog(getFragmentManager());
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (open_source_licenses != null) {
|
||||
open_source_licenses.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(final Preference preference) {
|
||||
startActivity(new Intent(PreferenceAbout.this, LicensesActivity.class));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (market != null) {
|
||||
market.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(final Preference preference) {
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
}
|
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.faizmalkani.floatingactionbutton.FloatingActionButton;
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.adapter.AdapterDetailedList;
|
||||
import sharedcode.turboeditor.fragment.EditDialogFragment;
|
||||
import sharedcode.turboeditor.util.AlphanumComparator;
|
||||
import sharedcode.turboeditor.util.Constants;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
import sharedcode.turboeditor.util.RootUtils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.Toolbox;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class SelectFileActivity extends Activity implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, EditDialogFragment.EditDialogListener {
|
||||
private String currentFolder;
|
||||
private ListView listView;
|
||||
private boolean wantAFile;
|
||||
private MenuItem mSearchViewMenuItem;
|
||||
private SearchView mSearchView;
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
|
||||
boolean light = PreferenceHelper.getLightTheme(this);
|
||||
if (light) {
|
||||
setTheme(R.style.AppTheme_Light);
|
||||
} else {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_select_file);
|
||||
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
final Actions action = (Actions) getIntent().getExtras().getSerializable("action");
|
||||
wantAFile = action == Actions.SelectFile;
|
||||
|
||||
listView = (ListView) findViewById(android.R.id.list);
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setTextFilterEnabled(true);
|
||||
|
||||
FloatingActionButton mFab = (FloatingActionButton)findViewById(R.id.fabbutton);
|
||||
mFab.setColor(getResources().getColor(R.color.fab_light));
|
||||
mFab.setDrawable(getResources().getDrawable(R.drawable.ic_fab_add));
|
||||
|
||||
mFab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
PopupMenu popup = new PopupMenu(SelectFileActivity.this, v);
|
||||
|
||||
popup.getMenuInflater().inflate(R.menu.popup_new_file, popup.getMenu());
|
||||
|
||||
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
final EditDialogFragment dialogFrag;
|
||||
int i = item.getItemId();
|
||||
if (i == R.id.im_new_file) {
|
||||
dialogFrag = EditDialogFragment.newInstance(EditDialogFragment.Actions.NewFile);
|
||||
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
|
||||
return true;
|
||||
} else if (i == R.id.im_new_folder) {
|
||||
dialogFrag = EditDialogFragment.newInstance(EditDialogFragment.Actions.NewFolder);
|
||||
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
popup.show();
|
||||
}
|
||||
});
|
||||
|
||||
mFab.listenTo(listView);
|
||||
|
||||
String lastNavigatedPath = PreferenceHelper.getWorkingFolder(this);
|
||||
|
||||
File file = new File(lastNavigatedPath);
|
||||
|
||||
if (!file.exists()) {
|
||||
PreferenceHelper.setWorkingFolder(this, PreferenceHelper.SD_CARD_ROOT);
|
||||
file = new File(PreferenceHelper.SD_CARD_ROOT);
|
||||
}
|
||||
|
||||
new UpdateList().execute(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (currentFolder.isEmpty() || currentFolder.equals("/")) {
|
||||
finish();
|
||||
} else {
|
||||
File file = new File(currentFolder);
|
||||
String parentFolder = file.getParent();
|
||||
new UpdateList().execute(parentFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
if (TextUtils.isEmpty(newText)) {
|
||||
listView.clearTextFilter();
|
||||
} else {
|
||||
listView.setFilterText(newText);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void returnData(String path) {
|
||||
final Intent returnIntent = new Intent();
|
||||
returnIntent.putExtra("path", path);
|
||||
setResult(RESULT_OK, returnIntent);
|
||||
// finish the activity
|
||||
finish();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent,
|
||||
View view, int position, long id) {
|
||||
final String name = ((TextView) view.findViewById(android.R.id.text1)).getText().toString();
|
||||
if (name.equals("..")) {
|
||||
if (currentFolder.equals("/")) {
|
||||
new UpdateList().execute(PreferenceHelper.getWorkingFolder(this));
|
||||
} else {
|
||||
File tempFile = new File(currentFolder);
|
||||
if (tempFile.isFile()) {
|
||||
tempFile = tempFile.getParentFile()
|
||||
.getParentFile();
|
||||
} else {
|
||||
tempFile = tempFile.getParentFile();
|
||||
}
|
||||
new UpdateList().execute(tempFile.getAbsolutePath());
|
||||
}
|
||||
return;
|
||||
} else if (name.equals(getString(R.string.home))) {
|
||||
new UpdateList().execute(PreferenceHelper.getWorkingFolder(this));
|
||||
return;
|
||||
}
|
||||
|
||||
final File selectedFile = new File(currentFolder, name);
|
||||
|
||||
if (selectedFile.isFile() && wantAFile) {
|
||||
returnData(selectedFile.getAbsolutePath());
|
||||
} else if (selectedFile.isDirectory()) {
|
||||
new UpdateList().execute(selectedFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.activity_select_file, menu);
|
||||
mSearchViewMenuItem = menu.findItem(R.id.im_search);
|
||||
mSearchView = (SearchView) mSearchViewMenuItem.getActionView();
|
||||
mSearchView.setIconifiedByDefault(true);
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
mSearchView.setSubmitButtonEnabled(false);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
// menu items
|
||||
MenuItem imSetAsWorkingFolder = menu.findItem(R.id.im_set_as_working_folder);
|
||||
MenuItem imIsWorkingFolder = menu.findItem(R.id.im_is_working_folder);
|
||||
MenuItem imSelectFolder = menu.findItem(R.id.im_select_folder);
|
||||
if(imSetAsWorkingFolder != null){
|
||||
// set the imSetAsWorkingFolder visible only if the two folder dont concide
|
||||
imSetAsWorkingFolder.setVisible(!currentFolder.equals(PreferenceHelper.getWorkingFolder(SelectFileActivity.this)));
|
||||
}
|
||||
if(imIsWorkingFolder != null) {
|
||||
// set visible is the other is invisible
|
||||
imIsWorkingFolder.setVisible(!imSetAsWorkingFolder.isVisible());
|
||||
}
|
||||
if(imSelectFolder != null) {
|
||||
imSelectFolder.setVisible(!wantAFile);
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int i = item.getItemId();
|
||||
if (i == android.R.id.home) {
|
||||
finish();
|
||||
return true;
|
||||
} else if (i == R.id.im_set_as_working_folder) {
|
||||
PreferenceHelper.setWorkingFolder(SelectFileActivity.this, currentFolder);
|
||||
invalidateOptionsMenu();
|
||||
return true;
|
||||
} else if (i == R.id.im_select_folder) {
|
||||
returnData(currentFolder);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFinishEditDialog(final String inputText, final String hint, final EditDialogFragment.Actions actions) {
|
||||
if (actions == EditDialogFragment.Actions.NewFile && !TextUtils.isEmpty(inputText)) {
|
||||
File file = new File(currentFolder, inputText);
|
||||
returnData(file.getAbsolutePath());
|
||||
} else if (actions == EditDialogFragment.Actions.NewFolder && !TextUtils.isEmpty(inputText)) {
|
||||
File file = new File(currentFolder, inputText);
|
||||
file.mkdirs();
|
||||
new UpdateList().execute(currentFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Actions {
|
||||
SelectFile, SelectFolder
|
||||
}
|
||||
|
||||
private class UpdateList extends AsyncTask<String, Void, LinkedList<AdapterDetailedList.FileDetail>> {
|
||||
|
||||
String exceptionMessage;
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
if (mSearchView != null) {
|
||||
mSearchView.setIconified(true);
|
||||
mSearchViewMenuItem.collapseActionView();
|
||||
mSearchView.setQuery("", false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected LinkedList<AdapterDetailedList.FileDetail> doInBackground(final String... params) {
|
||||
try {
|
||||
|
||||
final String path = params[0];
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File tempFile = new File(path);
|
||||
if (tempFile.isFile()) {
|
||||
tempFile = tempFile.getParentFile();
|
||||
}
|
||||
|
||||
String[] unopenableExtensions = {"apk", "mp3", "mp4", "png", "jpg", "jpeg"};
|
||||
|
||||
final LinkedList<AdapterDetailedList.FileDetail> fileDetails = new LinkedList<>();
|
||||
final LinkedList<AdapterDetailedList.FileDetail> folderDetails = new LinkedList<>();
|
||||
final ArrayList<File> files;
|
||||
currentFolder = tempFile.getAbsolutePath();
|
||||
|
||||
boolean isRoot = false;
|
||||
if (!tempFile.canRead()) {
|
||||
try {
|
||||
Shell shell = null;
|
||||
shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
isRoot = tb.isRootAccessGiven();
|
||||
} catch (IOException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
isRoot = false;
|
||||
}
|
||||
}
|
||||
|
||||
files = RootUtils.getFileList(currentFolder, isRoot);
|
||||
|
||||
Collections.sort(files, getFileNameComparator());
|
||||
|
||||
if (files != null) {
|
||||
for (final File f : files) {
|
||||
if (f.isDirectory()) {
|
||||
folderDetails.add(new AdapterDetailedList.FileDetail(f.getName(),
|
||||
getString(R.string.folder),
|
||||
""));
|
||||
} else if (f.isFile()
|
||||
&& !FilenameUtils.isExtension(f.getName().toLowerCase(), unopenableExtensions)
|
||||
&& FileUtils.sizeOf(f) <= Constants.MAX_FILE_SIZE * FileUtils.ONE_KB) {
|
||||
final long fileSize = f.length();
|
||||
SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy hh:mm a");
|
||||
String date = format.format(f.lastModified());
|
||||
fileDetails.add(new AdapterDetailedList.FileDetail(f.getName(),
|
||||
FileUtils.byteCountToDisplaySize(fileSize), date));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
folderDetails.addAll(fileDetails);
|
||||
return folderDetails;
|
||||
} catch (Exception e) {
|
||||
exceptionMessage = e.getMessage();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(final LinkedList<AdapterDetailedList.FileDetail> names) {
|
||||
if (names != null) {
|
||||
boolean isRoot = currentFolder.equals("/");
|
||||
AdapterDetailedList mAdapter = new AdapterDetailedList(getBaseContext(), names, isRoot);
|
||||
listView.setAdapter(mAdapter);
|
||||
} else if (exceptionMessage != null) {
|
||||
Toast.makeText(SelectFileActivity.this, exceptionMessage, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
invalidateOptionsMenu();
|
||||
super.onPostExecute(names);
|
||||
}
|
||||
|
||||
public final Comparator<File> getFileNameComparator() {
|
||||
return new AlphanumComparator() {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getTheString(Object obj) {
|
||||
return ((File) obj).getName()
|
||||
.toLowerCase();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Filter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.util.MimeTypes;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class AdapterDetailedList extends
|
||||
ArrayAdapter<AdapterDetailedList.FileDetail> {
|
||||
|
||||
// Layout Inflater
|
||||
private final LayoutInflater inflater;
|
||||
private final LinkedList<FileDetail> orig;
|
||||
private CustomFilter customFilter;
|
||||
// List of file details
|
||||
private LinkedList<FileDetail> fileDetails;
|
||||
|
||||
public AdapterDetailedList(final Context context,
|
||||
final LinkedList<FileDetail> fileDetails,
|
||||
final boolean isRoot) {
|
||||
super(context, R.layout.item_file_list, fileDetails);
|
||||
this.fileDetails = fileDetails;
|
||||
this.orig = fileDetails;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
if (!isRoot) {
|
||||
this.fileDetails.addFirst(new FileDetail("..", context.getString(R.string.folder), ""));
|
||||
} else {
|
||||
this.fileDetails.addFirst(new FileDetail(context.getString(R.string.home), context.getString(R.string.folder), ""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View getView(final int position,
|
||||
View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = this.inflater
|
||||
.inflate(R.layout.item_file_list,
|
||||
null);
|
||||
final ViewHolder hold = new ViewHolder();
|
||||
hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
hold.detailLabel = (TextView) convertView.findViewById(android.R.id.text2);
|
||||
hold.icon = (ImageView) convertView.findViewById(android.R.id.icon);
|
||||
convertView.setTag(hold);
|
||||
final FileDetail fileDetail = fileDetails.get(position);
|
||||
final String fileName = fileDetail.getName();
|
||||
setIcon(hold, fileDetail);
|
||||
hold.nameLabel.setText(fileName);
|
||||
hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified());
|
||||
} else {
|
||||
final ViewHolder hold = ((ViewHolder) convertView.getTag());
|
||||
final FileDetail fileDetail = fileDetails.get(position);
|
||||
final String fileName = fileDetail.getName();
|
||||
setIcon(hold, fileDetail);
|
||||
hold.nameLabel.setText(fileName);
|
||||
hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified());
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return fileDetails.size();
|
||||
}
|
||||
|
||||
private void setIcon(final ViewHolder viewHolder, final FileDetail fileDetail) {
|
||||
final String fileName = fileDetail.getName();
|
||||
final String ext = FilenameUtils.getExtension(fileName);
|
||||
if (fileDetail.isFolder()) {
|
||||
viewHolder.icon.setImageResource(R.color.file_folder);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_HTML).contains(ext) || ext.endsWith("html")) {
|
||||
viewHolder.icon.setImageResource(R.color.file_html);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_CODE).contains(ext)
|
||||
|| fileName.endsWith("css")
|
||||
|| fileName.endsWith("js")) {
|
||||
viewHolder.icon.setImageResource(R.color.file_code);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_ARCHIVE).contains(ext)) {
|
||||
viewHolder.icon.setImageResource(R.color.file_archive);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_MUSIC)
|
||||
.contains(ext)) {
|
||||
viewHolder.icon.setImageResource(R.color.file_media_music);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_PICTURE).contains(ext)) {
|
||||
viewHolder.icon.setImageResource(R.color.file_media_picture);
|
||||
} else if (Arrays.asList(MimeTypes.MIME_VIDEO).contains(ext)) {
|
||||
viewHolder.icon.setImageResource(R.color.file_media_video);
|
||||
} else {
|
||||
viewHolder.icon.setImageResource(R.color.file_text);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
if (customFilter == null) {
|
||||
customFilter = new CustomFilter();
|
||||
}
|
||||
return customFilter;
|
||||
}
|
||||
|
||||
public static class ViewHolder {
|
||||
|
||||
// Name of the file
|
||||
public TextView nameLabel;
|
||||
|
||||
// Size of the file
|
||||
public TextView detailLabel;
|
||||
|
||||
// Icon of the file
|
||||
public ImageView icon;
|
||||
}
|
||||
|
||||
public static class FileDetail {
|
||||
private final String name;
|
||||
private final String size;
|
||||
private final String dateModified;
|
||||
private final boolean isFolder;
|
||||
|
||||
public FileDetail(String name, String size,
|
||||
String dateModified) {
|
||||
this.name = name;
|
||||
this.size = size;
|
||||
this.dateModified = dateModified;
|
||||
isFolder = TextUtils.isEmpty(dateModified);
|
||||
}
|
||||
|
||||
public String getDateModified() {
|
||||
return dateModified;
|
||||
}
|
||||
|
||||
public String getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isFolder() {
|
||||
return isFolder;
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomFilter extends Filter {
|
||||
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence constraint) {
|
||||
FilterResults results = new FilterResults();
|
||||
if (constraint == null || constraint.length() == 0) {
|
||||
results.values = orig;
|
||||
results.count = orig.size();
|
||||
} else {
|
||||
LinkedList<FileDetail> nHolderList = new LinkedList<>();
|
||||
for (FileDetail h : orig) {
|
||||
if (h.getName().toLowerCase().contains(constraint.toString().toLowerCase()))
|
||||
nHolderList.add(h);
|
||||
}
|
||||
results.values = nHolderList;
|
||||
results.count = nHolderList.size();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void publishResults(CharSequence constraint,
|
||||
FilterResults results) {
|
||||
|
||||
|
||||
fileDetails = (LinkedList<FileDetail>) results.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AdapterDrawer extends
|
||||
ArrayAdapter<File> {
|
||||
|
||||
private final Callbacks callbacks;
|
||||
// Layout Inflater
|
||||
private final LayoutInflater inflater;
|
||||
// List of file details
|
||||
private final ArrayList<File> files;
|
||||
private String selectedPath = "";
|
||||
|
||||
public AdapterDrawer(Context context,
|
||||
ArrayList<File> files,
|
||||
Callbacks callbacks) {
|
||||
super(context, R.layout.item_file_list, files);
|
||||
this.files = files;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View getView(final int position,
|
||||
View convertView, final ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = this.inflater
|
||||
.inflate(R.layout.item_drawer_list,
|
||||
parent, false);
|
||||
final ViewHolder hold = new ViewHolder();
|
||||
hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
hold.cancelButton = (ImageView) convertView.findViewById(R.id.button_remove_from_list);
|
||||
convertView.setTag(hold);
|
||||
|
||||
final String fileName = files.get(position).getName();
|
||||
hold.nameLabel.setText(fileName);
|
||||
hold.cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath());
|
||||
callbacks.CancelItem(position, closeOpenedFile);
|
||||
if (closeOpenedFile)
|
||||
selectedPath = "";
|
||||
|
||||
}
|
||||
});
|
||||
if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()))
|
||||
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.BOLD);
|
||||
else
|
||||
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.NORMAL);
|
||||
} else {
|
||||
final ViewHolder hold = ((ViewHolder) convertView.getTag());
|
||||
final String fileName = files.get(position).getName();
|
||||
hold.nameLabel.setText(fileName);
|
||||
hold.cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath());
|
||||
callbacks.CancelItem(position, closeOpenedFile);
|
||||
if (closeOpenedFile)
|
||||
selectedPath = "";
|
||||
}
|
||||
});
|
||||
if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()))
|
||||
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.BOLD);
|
||||
else
|
||||
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.NORMAL);
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public void selectView(String selectedPath) {
|
||||
callbacks.ItemSelected(selectedPath);
|
||||
this.selectedPath = selectedPath;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public interface Callbacks {
|
||||
void CancelItem(int position, boolean andCloseOpenedFile);
|
||||
void ItemSelected(String path);
|
||||
}
|
||||
|
||||
public static class ViewHolder {
|
||||
|
||||
// Name of the file
|
||||
public TextView nameLabel;
|
||||
|
||||
public ImageView cancelButton;
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
import it.gmariotti.changelibs.library.view.ChangeLogListView;
|
||||
import sharedcode.turboeditor.util.Constants;
|
||||
|
||||
public class ChangelogDialogFragment extends DialogFragment {
|
||||
|
||||
public static void showChangeLogDialog(FragmentManager fragmentManager) {
|
||||
ChangelogDialogFragment changelogDialogFragment = new ChangelogDialogFragment();
|
||||
FragmentTransaction ft = fragmentManager.beginTransaction();
|
||||
Fragment prev = fragmentManager.findFragmentByTag("changelogdemo_dialog");
|
||||
if (prev != null) {
|
||||
ft.remove(prev);
|
||||
}
|
||||
ft.addToBackStack(null);
|
||||
changelogDialogFragment.show(ft, "changelogdemo_dialog");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
ChangeLogListView chgList = (ChangeLogListView) layoutInflater.inflate(R.layout.demo_changelog_fragment_dialogstandard, null);
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.changelog)
|
||||
.setView(chgList)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int whichButton) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
)
|
||||
.setPositiveButton(R.string.vota, new DialogInterface.OnClickListener() {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
try {
|
||||
if (Constants.FOR_AMAZON) {
|
||||
String url = "amzn://apps/android?p=com.maskyn.fileeditor";
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
} else {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditor"))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
})
|
||||
.create();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
// ...
|
||||
public class EditDialogFragment extends DialogFragment implements TextView.OnEditorActionListener {
|
||||
|
||||
private EditText mEditText;
|
||||
|
||||
public static EditDialogFragment newInstance(final Actions action) {
|
||||
return EditDialogFragment.newInstance(action, "");
|
||||
}
|
||||
|
||||
public static EditDialogFragment newInstance(final Actions action, final String hint) {
|
||||
final EditDialogFragment f = new EditDialogFragment();
|
||||
final Bundle args = new Bundle();
|
||||
args.putSerializable("action", action);
|
||||
args.putString("hint", hint);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final Actions action = (Actions) getArguments().getSerializable("action");
|
||||
final String hint;
|
||||
switch (action) {
|
||||
case NewFile:
|
||||
hint = getString(R.string.file);
|
||||
break;
|
||||
case NewFolder:
|
||||
hint = getString(R.string.folder);
|
||||
break;
|
||||
default:
|
||||
hint = null;
|
||||
break;
|
||||
}
|
||||
|
||||
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_edittext, null);
|
||||
this.mEditText = (EditText) view.findViewById(android.R.id.edit);
|
||||
this.mEditText.setHint(hint);
|
||||
|
||||
// Show soft keyboard automatically
|
||||
this.mEditText.setText(getArguments().getString("hint"));
|
||||
this.mEditText.requestFocus();
|
||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||
this.mEditText.setOnEditorActionListener(this);
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
returnData();
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
void returnData() {
|
||||
EditDialogListener target = (EditDialogListener) getTargetFragment();
|
||||
if (target == null) {
|
||||
target = (EditDialogListener) getActivity();
|
||||
}
|
||||
target.onFinishEditDialog(this.mEditText.getText().toString(), getArguments().getString("hint"),
|
||||
(Actions) getArguments().getSerializable("action"));
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) {
|
||||
if (EditorInfo.IME_ACTION_DONE == actionId) {
|
||||
returnData();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public enum Actions {
|
||||
NewFile, NewFolder
|
||||
}
|
||||
|
||||
public interface EditDialogListener {
|
||||
void onFinishEditDialog(String result, String hint, Actions action);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Switch;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
import sharedcode.turboeditor.preferences.SettingsFragment;
|
||||
import sharedcode.turboeditor.util.SaveFileTask;
|
||||
|
||||
import org.mozilla.universalchardet.Constants;
|
||||
|
||||
public class EncodingDialogFragment extends DialogFragment implements AdapterView.OnItemClickListener {
|
||||
|
||||
private final String[] encodings = new String[]{
|
||||
Constants.CHARSET_BIG5,
|
||||
Constants.CHARSET_EUC_JP,
|
||||
Constants.CHARSET_EUC_KR,
|
||||
Constants.CHARSET_EUC_TW,
|
||||
Constants.CHARSET_GB18030,
|
||||
"GB2312",
|
||||
Constants.CHARSET_IBM855,
|
||||
Constants.CHARSET_IBM866,
|
||||
Constants.CHARSET_ISO_2022_CN,
|
||||
Constants.CHARSET_ISO_2022_JP,
|
||||
Constants.CHARSET_ISO_2022_KR,
|
||||
Constants.CHARSET_ISO_8859_5,
|
||||
Constants.CHARSET_ISO_8859_7,
|
||||
Constants.CHARSET_ISO_8859_8,
|
||||
Constants.CHARSET_KOI8_R,
|
||||
Constants.CHARSET_MACCYRILLIC,
|
||||
Constants.CHARSET_SHIFT_JIS,
|
||||
Constants.CHARSET_UTF_16BE,
|
||||
Constants.CHARSET_UTF_16LE,
|
||||
Constants.CHARSET_UTF_32BE,
|
||||
Constants.CHARSET_UTF_32LE,
|
||||
Constants.CHARSET_UTF_8,
|
||||
Constants.CHARSET_WINDOWS_1251,
|
||||
Constants.CHARSET_WINDOWS_1252,
|
||||
Constants.CHARSET_WINDOWS_1253,
|
||||
Constants.CHARSET_WINDOWS_1255
|
||||
};
|
||||
private ListView list;
|
||||
|
||||
public static EncodingDialogFragment newInstance() {
|
||||
final EncodingDialogFragment f = new EncodingDialogFragment();
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_encoding_list, null);
|
||||
list = (ListView) view.findViewById(android.R.id.list);
|
||||
Switch autoencoding = (Switch) view.findViewById(android.R.id.checkbox);
|
||||
autoencoding.setChecked(PreferenceHelper.getAutoEncoding(getActivity()));
|
||||
|
||||
autoencoding.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
PreferenceHelper.setAutoencoding(getActivity(), isChecked);
|
||||
}
|
||||
});
|
||||
|
||||
list.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.item_single_choice, encodings));
|
||||
list.setOnItemClickListener(this);
|
||||
|
||||
String currentEncoding = PreferenceHelper.getEncoding(getActivity());
|
||||
|
||||
for (int i = 0; i < encodings.length; i++) {
|
||||
if (currentEncoding.equals(encodings[i])) {
|
||||
list.setItemChecked(i, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setView(view)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
DialogListener target = (DialogListener) getTargetFragment();
|
||||
if (target == null) {
|
||||
target = (DialogListener) getActivity();
|
||||
}
|
||||
target.onEncodingSelected(encodings[position]);
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
public interface DialogListener {
|
||||
void onEncodingSelected(String result);
|
||||
}
|
||||
}
|
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
import sharedcode.turboeditor.util.SearchResult;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// ...
|
||||
public class FindTextDialogFragment extends DialogFragment {
|
||||
|
||||
private EditText textToFind, textToReplace;
|
||||
private CheckBox regexCheck, replaceCheck, matchCaseCheck;
|
||||
|
||||
public static FindTextDialogFragment newInstance(String allText) {
|
||||
final FindTextDialogFragment f = new FindTextDialogFragment();
|
||||
final Bundle args = new Bundle();
|
||||
args.putString("allText", allText);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_find_text, null);
|
||||
this.textToFind = (EditText) view.findViewById(R.id.text_to_find);
|
||||
this.textToReplace = (EditText) view.findViewById(R.id.text_to_replace);
|
||||
this.regexCheck = (CheckBox) view.findViewById(R.id.regex_check);
|
||||
this.replaceCheck = (CheckBox) view.findViewById(R.id.replace_check);
|
||||
this.matchCaseCheck = (CheckBox) view.findViewById(R.id.match_case_check);
|
||||
|
||||
replaceCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
textToReplace.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setView(view)
|
||||
.setPositiveButton(R.string.find,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart()
|
||||
{
|
||||
super.onStart(); //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point
|
||||
AlertDialog d = (AlertDialog)getDialog();
|
||||
if(d != null)
|
||||
{
|
||||
Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setText(getString(R.string.find));
|
||||
positiveButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
returnData();
|
||||
}
|
||||
});
|
||||
|
||||
Button negativeButton = (Button) d.getButton(Dialog.BUTTON_NEGATIVE);
|
||||
negativeButton.setText(getString(android.R.string.cancel));
|
||||
negativeButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void returnData() {
|
||||
if(textToFind.getText().toString().isEmpty()) {
|
||||
this.dismiss();
|
||||
} else {
|
||||
// we disable the okButton while we search
|
||||
new SearchTask().execute();
|
||||
}
|
||||
}
|
||||
|
||||
private class SearchTask extends AsyncTask<Void, Void, Void>{
|
||||
|
||||
LinkedList<Integer> foundIndex;
|
||||
boolean foundSomething;
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
String allText = getArguments().getString("allText");
|
||||
String whatToSearch = textToFind.getText().toString();
|
||||
boolean caseSensitive = matchCaseCheck.isChecked();
|
||||
boolean isRegex = regexCheck.isChecked();
|
||||
foundIndex = new LinkedList<>();
|
||||
Matcher matcher = null;
|
||||
foundSomething = false;
|
||||
|
||||
if(isRegex) {
|
||||
try {
|
||||
if(caseSensitive)
|
||||
matcher = Pattern.compile(whatToSearch, Pattern.MULTILINE).matcher(allText);
|
||||
else
|
||||
matcher = Pattern.compile(whatToSearch, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE).matcher(allText);
|
||||
} catch (Exception e) {
|
||||
isRegex = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(isRegex) {
|
||||
while (matcher.find()) {
|
||||
foundSomething = true;
|
||||
|
||||
foundIndex.add(matcher.start());
|
||||
}
|
||||
} else {
|
||||
if(caseSensitive == false) { // by default is case sensitive
|
||||
whatToSearch = whatToSearch.toLowerCase();
|
||||
allText = allText.toLowerCase();
|
||||
}
|
||||
int index = allText.indexOf(whatToSearch);
|
||||
while (index >= 0) {
|
||||
foundSomething = true;
|
||||
|
||||
foundIndex.add(index);
|
||||
|
||||
index = allText.indexOf(whatToSearch, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
if(foundSomething) {
|
||||
// the class that called this Dialog should implement the SearchDialogIterface
|
||||
SearchDialogInterface searchDialogInterface;
|
||||
searchDialogInterface = ((SearchDialogInterface) getTargetFragment());
|
||||
if(searchDialogInterface == null)
|
||||
searchDialogInterface = ((SearchDialogInterface) getActivity());
|
||||
|
||||
// if who called this has not implemented the interface we return nothing
|
||||
if(searchDialogInterface == null)
|
||||
return;
|
||||
// else we return positions and other things
|
||||
else {
|
||||
SearchResult searchResult = new SearchResult(foundIndex, textToFind.length(), replaceCheck.isChecked(), textToReplace.getText().toString());
|
||||
searchDialogInterface.onSearchDone(searchResult);
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
Toast.makeText(getActivity(), String.format(getString(R.string.occurrences_found), foundIndex.size()), Toast.LENGTH_SHORT).show();
|
||||
// dismiss the dialog
|
||||
FindTextDialogFragment.this.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public interface SearchDialogInterface {
|
||||
void onSearchDone(SearchResult searchResult);
|
||||
}
|
||||
}
|
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.adapter.AdapterDrawer;
|
||||
import sharedcode.turboeditor.util.EventBusEvents;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
|
||||
public class NavigationDrawerListFragment extends Fragment implements AdapterView.OnItemClickListener, AdapterDrawer.Callbacks {
|
||||
|
||||
|
||||
private AdapterDrawer arrayAdapter;
|
||||
private ArrayList<File> files;
|
||||
private ListView listView;
|
||||
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
// Our custom layout
|
||||
View rootView = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
|
||||
listView = (ListView) rootView.findViewById(android.R.id.list);
|
||||
listView.setEmptyView(rootView.findViewById(android.R.id.empty));
|
||||
files = new ArrayList<>();
|
||||
arrayAdapter = new AdapterDrawer(getActivity(), files, this);
|
||||
listView.setAdapter(arrayAdapter);
|
||||
return rootView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
listView.setOnItemClickListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
// Register the Event Bus for events
|
||||
EventBus.getDefault().registerSticky(this);
|
||||
// Refresh the list view
|
||||
refreshList();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
// Unregister the Event Bus
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
// File paths saved in preferences
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
// Path of the file selected
|
||||
String filePath = savedPaths[position];
|
||||
// Send the event that a file was selected
|
||||
EventBus.getDefault().post(new EventBusEvents.NewFileToOpen(new File(filePath)));
|
||||
arrayAdapter.selectView(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a new file is opened
|
||||
* Invoked by the main activity which receive the intent
|
||||
*
|
||||
* @param event The event called
|
||||
*/
|
||||
public void onEvent(EventBusEvents.NewFileToOpen event) {
|
||||
|
||||
// File paths saved in preferences
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
String selectedPath = event.getFile().getAbsolutePath();
|
||||
for (String savedPath : savedPaths) {
|
||||
// We don't need to save the file path twice
|
||||
if (savedPath.equals(selectedPath)) {
|
||||
arrayAdapter.selectView(selectedPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add the path if it wasn't added before
|
||||
addPath(selectedPath);
|
||||
|
||||
arrayAdapter.selectView(selectedPath);
|
||||
|
||||
EventBus.getDefault().removeStickyEvent(event);
|
||||
}
|
||||
|
||||
public void onEvent(EventBusEvents.SavedAFile event) {
|
||||
if (addPath(event.getPath())) {
|
||||
arrayAdapter.selectView(event.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
public void onEvent(EventBusEvents.ClosedAFile event) {
|
||||
arrayAdapter.selectView("");
|
||||
}
|
||||
|
||||
private boolean addPath(String path) {
|
||||
// File paths saved in preferences
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
// StringBuilder
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean pathAlreadyExist = false;
|
||||
for (String savedPath : savedPaths) {
|
||||
// Append the file path and a comma
|
||||
sb.append(savedPath).append(",");
|
||||
if (savedPath.equals(path))
|
||||
pathAlreadyExist = true;
|
||||
}
|
||||
// Append new path
|
||||
if (!pathAlreadyExist)
|
||||
sb.append(path);
|
||||
// Put the string and commit
|
||||
PreferenceHelper.setSavedPaths(getActivity(), sb);
|
||||
// Update list
|
||||
refreshList();
|
||||
|
||||
return pathAlreadyExist == false;
|
||||
}
|
||||
|
||||
private void removePath(String path) {
|
||||
// File paths saved in preferences
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
// StringBuilder
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// for cycle
|
||||
for (String savedPath : savedPaths) {
|
||||
if (path.equals(savedPath)) continue;
|
||||
sb.append(savedPath).append(",");
|
||||
}
|
||||
// Put the string and commit
|
||||
PreferenceHelper.setSavedPaths(getActivity(), sb);
|
||||
// Update list
|
||||
refreshList();
|
||||
}
|
||||
|
||||
private void refreshList() {
|
||||
// File paths saved in preferences
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
// File names for the list
|
||||
files.clear();
|
||||
// StringBuilder that will contain the file paths
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// for cycle to convert paths to names
|
||||
for (String path : savedPaths) {
|
||||
File file = new File(path);
|
||||
// Check that the file exist
|
||||
if (file.exists()) {
|
||||
files.add(file);
|
||||
sb.append(path).append(",");
|
||||
}
|
||||
}
|
||||
// save list without empty or non existed files
|
||||
PreferenceHelper.setSavedPaths(getActivity(), sb);
|
||||
// Set adapter
|
||||
arrayAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void CancelItem(int position, boolean andCloseOpenedFile) {
|
||||
String[] savedPaths = PreferenceHelper.getSavedPaths(getActivity());
|
||||
removePath(savedPaths[position]);
|
||||
if (andCloseOpenedFile)
|
||||
EventBus.getDefault().post(new EventBusEvents.CannotOpenAFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ItemSelected(String path) {
|
||||
EventBus.getDefault().post(new EventBusEvents.AFileIsSelected(path));
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.activity.PreferenceAbout;
|
||||
import sharedcode.turboeditor.preferences.PreferenceHelper;
|
||||
import sharedcode.turboeditor.util.SaveFileTask;
|
||||
|
||||
// ...
|
||||
public class NewFileDetailsDialogFragment extends DialogFragment {
|
||||
|
||||
private EditText mName;
|
||||
private EditText mFolder;
|
||||
|
||||
public static NewFileDetailsDialogFragment newInstance(String fileText, String fileEncoding) {
|
||||
final NewFileDetailsDialogFragment f = new NewFileDetailsDialogFragment();
|
||||
final Bundle args = new Bundle();
|
||||
args.putString("fileText", fileText);
|
||||
args.putString("fileEncoding", fileEncoding);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_new_file_details, null);
|
||||
this.mName = (EditText) view.findViewById(android.R.id.text1);
|
||||
this.mFolder = (EditText) view.findViewById(android.R.id.text2);
|
||||
|
||||
this.mFolder.setText(PreferenceHelper.getWorkingFolder(getActivity()));
|
||||
|
||||
// Show soft keyboard automatically
|
||||
this.mName.requestFocus();
|
||||
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if(!mName.getText().toString().isEmpty() && !mFolder.getText().toString().isEmpty()) {
|
||||
File file = new File(mFolder.getText().toString(), mName.getText().toString());
|
||||
new SaveFileTask(getActivity(), file.getPath(), getArguments().getString("fileText"), getArguments().getString("fileEncoding")).execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
public class NoFileOpenedFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_no_file_open, container, false);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.preferences.SettingsFragment;
|
||||
import sharedcode.turboeditor.util.SaveFileTask;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class SaveFileDialogFragment extends DialogFragment {
|
||||
|
||||
public static SaveFileDialogFragment newInstance(String filePath, String text) {
|
||||
SaveFileDialogFragment frag = new SaveFileDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("filePath", filePath);
|
||||
args.putString("text", text);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final String filePath = getArguments().getString("filePath");
|
||||
final String text = getArguments().getString("text");
|
||||
final String fileName = FilenameUtils.getName(filePath);
|
||||
final File file = new File(filePath);
|
||||
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setMessage(String.format(getString(R.string.save_changes), fileName))
|
||||
.setPositiveButton(android.R.string.yes,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if(!fileName.isEmpty())
|
||||
new SaveFileTask(getActivity(), filePath, text, SettingsFragment.sCurrentEncoding).execute();
|
||||
else {
|
||||
NewFileDetailsDialogFragment dialogFrag = NewFileDetailsDialogFragment.newInstance(text, SettingsFragment.sCurrentEncoding);
|
||||
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.no,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
public static enum Action {
|
||||
SaveAFile
|
||||
}
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
// ...
|
||||
public class SeekbarDialogFragment extends DialogFragment {
|
||||
|
||||
private NumberPicker mSeekBar;
|
||||
|
||||
public static SeekbarDialogFragment newInstance(final Actions action) {
|
||||
return SeekbarDialogFragment.newInstance(action, 0, 50, 100);
|
||||
}
|
||||
|
||||
public static SeekbarDialogFragment newInstance(final Actions action, final int min, final int current, final int max) {
|
||||
final SeekbarDialogFragment f = new SeekbarDialogFragment();
|
||||
final Bundle args = new Bundle();
|
||||
args.putSerializable("action", action);
|
||||
args.putInt("min", min);
|
||||
args.putInt("current", current);
|
||||
args.putInt("max", max);
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final Actions action = (Actions) getArguments().getSerializable("action");
|
||||
|
||||
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_seekbar, null);
|
||||
this.mSeekBar = (NumberPicker) view.findViewById(android.R.id.input);
|
||||
this.mSeekBar.setMaxValue(getArguments().getInt("max"));
|
||||
this.mSeekBar.setMinValue(getArguments().getInt("min"));
|
||||
this.mSeekBar.setValue(getArguments().getInt("current"));
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
//.setTitle(title)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
returnData();
|
||||
}
|
||||
}
|
||||
)
|
||||
.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
.create();
|
||||
}
|
||||
|
||||
void returnData() {
|
||||
onSeekbarDialogDismissed target = (onSeekbarDialogDismissed) getTargetFragment();
|
||||
if (target == null) {
|
||||
target = (onSeekbarDialogDismissed) getActivity();
|
||||
}
|
||||
target.onSeekbarDialogDismissed(
|
||||
(Actions) getArguments().getSerializable("action"),
|
||||
mSeekBar.getValue()
|
||||
);
|
||||
this.dismiss();
|
||||
}
|
||||
|
||||
public enum Actions {
|
||||
FileSize, SelectPage, GoToLine
|
||||
}
|
||||
|
||||
public interface onSeekbarDialogDismissed {
|
||||
void onSeekbarDialogDismissed(Actions action, int value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Environment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public final class PreferenceHelper {
|
||||
|
||||
public static final String SD_CARD_ROOT = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
|
||||
private PreferenceHelper() {
|
||||
}
|
||||
|
||||
// Getter Methods
|
||||
|
||||
private static SharedPreferences getPrefs(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
private static SharedPreferences.Editor getEditor(Context context) {
|
||||
return getPrefs(context).edit();
|
||||
}
|
||||
|
||||
public static boolean getUseMonospace(Context context) {
|
||||
return getPrefs(context).getBoolean("use_monospace", false);
|
||||
}
|
||||
|
||||
public static boolean getLineNumbers(Context context) {
|
||||
return getPrefs(context).getBoolean("editor_line_numbers", true);
|
||||
}
|
||||
|
||||
public static boolean getSyntaxHiglight(Context context) {
|
||||
return getPrefs(context).getBoolean("editor_syntax_highlight", false);
|
||||
}
|
||||
|
||||
public static boolean getWrapContent(Context context) {
|
||||
return getPrefs(context).getBoolean("editor_wrap_content", true);
|
||||
}
|
||||
|
||||
public static boolean getLightTheme(Context context) {
|
||||
return getPrefs(context).getBoolean("light_theme", false);
|
||||
}
|
||||
|
||||
public static boolean getSuggestionActive(Context context) {
|
||||
return getPrefs(context).getBoolean("suggestion_active", false);
|
||||
}
|
||||
|
||||
public static boolean getAutoEncoding(Context context) {
|
||||
return getPrefs(context).getBoolean("autoencoding", true);
|
||||
}
|
||||
|
||||
public static boolean getSendErrorReports(Context context) {
|
||||
return getPrefs(context).getBoolean("send_error_reports", true);
|
||||
}
|
||||
|
||||
public static int getLastDayAdShowed(Context context) {
|
||||
return getPrefs(context).getInt("last_day_ad_showed", 0);
|
||||
}
|
||||
|
||||
public static String getEncoding(Context context) {
|
||||
return getPrefs(context).getString("editor_encoding", "UTF-8");
|
||||
}
|
||||
|
||||
public static int getFontSize(Context context) {
|
||||
return getPrefs(context).getInt("font_size", 16);
|
||||
}
|
||||
|
||||
public static String getWorkingFolder(Context context) {
|
||||
return getPrefs(context).getString("working_folder", SD_CARD_ROOT);
|
||||
}
|
||||
|
||||
public static String[] getSavedPaths(Context context) {
|
||||
return getPrefs(context).getString("savedPaths", "").split(",");
|
||||
}
|
||||
|
||||
public static boolean getPageSystemButtonsPopupShown(Context context) {
|
||||
return getPrefs(context).getBoolean("page_system_button_popup_shown", false);
|
||||
}
|
||||
|
||||
public static boolean getAutoSave(Context context) {
|
||||
return getPrefs(context).getBoolean("auto_save", false);
|
||||
}
|
||||
|
||||
public static boolean getReadOnly(Context context) {
|
||||
return getPrefs(context).getBoolean("read_only", false);
|
||||
}
|
||||
|
||||
// Setter methods
|
||||
|
||||
public static void setUseMonospace(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("use_monospace", value).commit();
|
||||
}
|
||||
|
||||
public static void setLineNumbers(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("editor_line_numbers", value).commit();
|
||||
}
|
||||
|
||||
public static void setSyntaxHiglight(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("editor_syntax_highlight", value).commit();
|
||||
}
|
||||
|
||||
public static void setWrapContent(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("editor_wrap_content", value).commit();
|
||||
}
|
||||
|
||||
public static void setLightTheme(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("light_theme", value).commit();
|
||||
}
|
||||
|
||||
public static void setSuggestionActive(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("suggestion_active", value).commit();
|
||||
}
|
||||
|
||||
public static void setAutoencoding(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("autoencoding", value).commit();
|
||||
}
|
||||
|
||||
public static void setSendErrorReports(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("send_error_reports", value).commit();
|
||||
}
|
||||
|
||||
public static void setLastDayAdShowed(Context context, int value) {
|
||||
getEditor(context).putInt("last_day_ad_showed", value).commit();
|
||||
}
|
||||
|
||||
public static void setEncoding(Context context, String value) {
|
||||
getEditor(context).putString("editor_encoding", value).commit();
|
||||
}
|
||||
|
||||
public static void setFontSize(Context context, int value) {
|
||||
getEditor(context).putInt("font_size", value).commit();
|
||||
}
|
||||
|
||||
public static void setWorkingFolder(Context context, String value) {
|
||||
getEditor(context).putString("working_folder", value).commit();
|
||||
}
|
||||
|
||||
public static void setSavedPaths(Context context, StringBuilder stringBuilder) {
|
||||
getEditor(context).putString("savedPaths", stringBuilder.toString()).commit();
|
||||
}
|
||||
|
||||
public static void setPageSystemButtonsPopupShown(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("page_system_button_popup_shown", value).commit();
|
||||
}
|
||||
|
||||
public static void setAutoSave(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("auto_save", value).commit();
|
||||
}
|
||||
|
||||
public static void setReadOnly(Context context, boolean value) {
|
||||
getEditor(context).putBoolean("read_only", value).commit();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.preferences;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
import sharedcode.turboeditor.fragment.EncodingDialogFragment;
|
||||
import sharedcode.turboeditor.fragment.SeekbarDialogFragment;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import sharedcode.turboeditor.util.ProCheckUtils;
|
||||
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.AUTO_SAVE;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.ENCODING;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.LINE_NUMERS;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.MONOSPACE;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.READ_ONLY;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.TEXT_SUGGESTIONS;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE;
|
||||
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT;
|
||||
|
||||
public class SettingsFragment extends Fragment implements EncodingDialogFragment.DialogListener, SeekbarDialogFragment.onSeekbarDialogDismissed {
|
||||
|
||||
public static String sCurrentEncoding;
|
||||
// Editor Variables
|
||||
public static boolean sLineNumbers;
|
||||
public static boolean sColorSyntax;
|
||||
public static boolean sWrapContent;
|
||||
public static int sFontSize;
|
||||
public static boolean sUseMonospace;
|
||||
public static boolean sLightTheme;
|
||||
public static boolean sSuggestionsActive;
|
||||
public static boolean sAutoSave;
|
||||
public static boolean sReadOnly;
|
||||
public static boolean sSendErrorReports;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
SettingsFragment.sCurrentEncoding = PreferenceHelper.getEncoding(getActivity());
|
||||
SettingsFragment.sUseMonospace = PreferenceHelper.getUseMonospace(getActivity());
|
||||
SettingsFragment.sColorSyntax = PreferenceHelper.getSyntaxHiglight(getActivity());
|
||||
SettingsFragment.sWrapContent = PreferenceHelper.getWrapContent(getActivity());
|
||||
SettingsFragment.sLineNumbers = PreferenceHelper.getLineNumbers(getActivity());
|
||||
SettingsFragment.sFontSize = PreferenceHelper.getFontSize(getActivity());
|
||||
SettingsFragment.sSuggestionsActive = PreferenceHelper.getSuggestionActive(getActivity());
|
||||
SettingsFragment.sLightTheme = PreferenceHelper.getLightTheme(getActivity());
|
||||
SettingsFragment.sAutoSave = PreferenceHelper.getAutoSave(getActivity());
|
||||
SettingsFragment.sReadOnly = PreferenceHelper.getReadOnly(getActivity());
|
||||
SettingsFragment.sSendErrorReports = PreferenceHelper.getReadOnly(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Our custom layout
|
||||
View rootView = inflater.inflate(R.layout.fragment_settings, container, false);
|
||||
final CheckBox switchLineNumbers, switchSyntax, switchWrapContent, switchMonospace, switchLightTheme, switchSuggestionsActive, switchAutoSave, switchReadOnly, switchSendErrorReports;
|
||||
switchLineNumbers = (CheckBox) rootView.findViewById(R.id.switch_line_numbers);
|
||||
switchSyntax = (CheckBox) rootView.findViewById(R.id.switch_syntax);
|
||||
switchWrapContent = (CheckBox) rootView.findViewById(R.id.switch_wrap_content);
|
||||
switchMonospace = (CheckBox) rootView.findViewById(R.id.switch_monospace);
|
||||
switchLightTheme = (CheckBox) rootView.findViewById(R.id.switch_light_theme);
|
||||
switchSuggestionsActive = (CheckBox) rootView.findViewById(R.id.switch_suggestions_active);
|
||||
switchAutoSave = (CheckBox) rootView.findViewById(R.id.switch_auto_save);
|
||||
switchReadOnly = (CheckBox) rootView.findViewById(R.id.switch_read_only);
|
||||
switchSendErrorReports = (CheckBox) rootView.findViewById(R.id.switch_send_error_reports);
|
||||
|
||||
switchLineNumbers.setChecked(sLineNumbers);
|
||||
switchSyntax.setChecked(sColorSyntax);
|
||||
switchWrapContent.setChecked(sWrapContent);
|
||||
switchMonospace.setChecked(sUseMonospace);
|
||||
switchLightTheme.setChecked(sLightTheme);
|
||||
switchSuggestionsActive.setChecked(sSuggestionsActive);
|
||||
switchAutoSave.setChecked(sAutoSave);
|
||||
switchReadOnly.setChecked(sReadOnly);
|
||||
|
||||
|
||||
TextView encodingView, fontSizeView, goProView;
|
||||
encodingView = (TextView) rootView.findViewById(R.id.drawer_button_encoding);
|
||||
fontSizeView = (TextView) rootView.findViewById(R.id.drawer_button_font_size);
|
||||
goProView = (TextView) rootView.findViewById(R.id.drawer_button_go_pro);
|
||||
|
||||
switchLineNumbers.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sLineNumbers = isChecked;
|
||||
PreferenceHelper.setLineNumbers(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(LINE_NUMERS));
|
||||
}
|
||||
});
|
||||
|
||||
switchSyntax.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sColorSyntax = isChecked;
|
||||
PreferenceHelper.setSyntaxHiglight(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(SYNTAX));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
switchWrapContent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sWrapContent = isChecked;
|
||||
PreferenceHelper.setWrapContent(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(WRAP_CONTENT));
|
||||
}
|
||||
});
|
||||
|
||||
switchMonospace.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sUseMonospace = isChecked;
|
||||
PreferenceHelper.setUseMonospace(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(MONOSPACE));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
switchLightTheme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sLightTheme = isChecked;
|
||||
PreferenceHelper.setLightTheme(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(THEME_CHANGE));
|
||||
}
|
||||
});
|
||||
|
||||
switchSuggestionsActive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sSuggestionsActive = isChecked;
|
||||
PreferenceHelper.setSuggestionActive(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(TEXT_SUGGESTIONS));
|
||||
}
|
||||
});
|
||||
|
||||
switchAutoSave.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sAutoSave = isChecked;
|
||||
PreferenceHelper.setAutoSave(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(AUTO_SAVE));
|
||||
}
|
||||
});
|
||||
|
||||
switchReadOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sReadOnly = isChecked;
|
||||
PreferenceHelper.setReadOnly(getActivity(), isChecked);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(READ_ONLY));
|
||||
}
|
||||
});
|
||||
|
||||
switchSendErrorReports.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
sSendErrorReports = isChecked;
|
||||
PreferenceHelper.setSendErrorReports(getActivity(), isChecked);
|
||||
}
|
||||
});
|
||||
|
||||
encodingView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
EncodingDialogFragment dialogFrag = EncodingDialogFragment.newInstance();
|
||||
dialogFrag.setTargetFragment(SettingsFragment.this, 0);
|
||||
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
|
||||
}
|
||||
});
|
||||
|
||||
fontSizeView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int fontMax = 36;
|
||||
int fontCurrent = //(int) (mEditor.getTextSize() / scaledDensity);
|
||||
//fontMax / 2;
|
||||
PreferenceHelper.getFontSize(getActivity());
|
||||
SeekbarDialogFragment dialogFrag = SeekbarDialogFragment.newInstance(SeekbarDialogFragment.Actions.FileSize, 1, fontCurrent, fontMax);
|
||||
dialogFrag.setTargetFragment(SettingsFragment.this, 0);
|
||||
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
|
||||
}
|
||||
});
|
||||
|
||||
goProView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
try {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditorpro"))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
goProView.setVisibility(ProCheckUtils.isPro(getActivity()) ? View.GONE : View.VISIBLE);
|
||||
return rootView;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onEncodingSelected(String value) {
|
||||
PreferenceHelper.setEncoding(getActivity(), value);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(ENCODING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekbarDialogDismissed(SeekbarDialogFragment.Actions action, int value) {
|
||||
sFontSize = value;
|
||||
PreferenceHelper.setFontSize(getActivity(), value);
|
||||
EventBus.getDefault().post(new APreferenceValueWasChanged(FONT_SIZE));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface must be implemented by activities that contain this
|
||||
* fragment to allow an interaction in this fragment to be communicated
|
||||
* to the activity and potentially other fragments contained in that
|
||||
* activity.
|
||||
* <p/>
|
||||
* See the Android Training lesson <a href=
|
||||
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||
* >Communicating with Other Fragments</a> for more information.
|
||||
*/
|
||||
public interface OnFragmentInteractionListener {
|
||||
// TODO: Update argument type and name
|
||||
public void onFragmentInteraction(Uri uri);
|
||||
}
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;/*
|
||||
* The Alphanum Algorithm is an improved sorting algorithm for strings
|
||||
* containing numbers. Instead of sorting numbers in ASCII order like
|
||||
* a standard sort, this algorithm sorts numbers in numeric order.
|
||||
*
|
||||
* The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
|
||||
*
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David
|
||||
* Koelle
|
||||
* <p/>
|
||||
* To convert to use Templates (Java 1.5+): - Change "implements Comparator" to "implements
|
||||
* Comparator<String>" - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" -
|
||||
* Remove the type checking and casting in compare().
|
||||
* <p/>
|
||||
* To use this class: Use the static "sort" method from the java.util.Collections class:
|
||||
* Collections.sort(your list, new AlphanumComparator());
|
||||
*/
|
||||
public class AlphanumComparator implements Comparator {
|
||||
private boolean isDigit(char ch) {
|
||||
return ch >= 48 && ch <= 57;
|
||||
}
|
||||
|
||||
/**
|
||||
* Length of string is passed in for improved efficiency (only need to calculate it once) *
|
||||
*/
|
||||
private String getChunk(String s, int slength, int marker) {
|
||||
StringBuilder chunk = new StringBuilder();
|
||||
char c = s.charAt(marker);
|
||||
chunk.append(c);
|
||||
marker++;
|
||||
if (isDigit(c)) {
|
||||
while (marker < slength) {
|
||||
c = s.charAt(marker);
|
||||
if (!isDigit(c)) {
|
||||
break;
|
||||
}
|
||||
chunk.append(c);
|
||||
marker++;
|
||||
}
|
||||
} else {
|
||||
while (marker < slength) {
|
||||
c = s.charAt(marker);
|
||||
if (isDigit(c)) {
|
||||
break;
|
||||
}
|
||||
chunk.append(c);
|
||||
marker++;
|
||||
}
|
||||
}
|
||||
return chunk.toString();
|
||||
}
|
||||
|
||||
public String getTheString(Object obj) {
|
||||
return (String) obj;
|
||||
}
|
||||
|
||||
public int compare(Object o1, Object o2) {
|
||||
String s1 = getTheString(o1);
|
||||
String s2 = getTheString(o2);
|
||||
|
||||
int thisMarker = 0;
|
||||
int thatMarker = 0;
|
||||
int s1Length = s1.length();
|
||||
int s2Length = s2.length();
|
||||
|
||||
while (thisMarker < s1Length && thatMarker < s2Length) {
|
||||
String thisChunk = getChunk(s1, s1Length, thisMarker);
|
||||
thisMarker += thisChunk.length();
|
||||
|
||||
String thatChunk = getChunk(s2, s2Length, thatMarker);
|
||||
thatMarker += thatChunk.length();
|
||||
|
||||
// If both chunks contain numeric characters, sort them numerically
|
||||
int result = 0;
|
||||
if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
|
||||
// Simple chunk comparison by length.
|
||||
int thisChunkLength = thisChunk.length();
|
||||
result = thisChunkLength - thatChunk.length();
|
||||
// If equal, the first different number counts
|
||||
if (result == 0) {
|
||||
for (int i = 0; i < thisChunkLength; i++) {
|
||||
result = thisChunk.charAt(i) - thatChunk.charAt(i);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = thisChunk.compareTo(thatChunk);
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return s1Length - s2Length;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
public class AppInfoHelper {
|
||||
public static String getApplicationName(final Context context) {
|
||||
final ApplicationInfo applicationInfo = context.getApplicationInfo();
|
||||
return context.getString(applicationInfo.labelRes);
|
||||
}
|
||||
|
||||
public static String getCurrentVersion(final Context context) {
|
||||
try {
|
||||
final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
|
||||
0);
|
||||
return packageInfo.versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
public class Constants {
|
||||
public static final int MAX_FILE_SIZE = 20_000;
|
||||
public static final boolean FOR_AMAZON = false;
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import sharedcode.turboeditor.views.GoodScrollView;
|
||||
|
||||
public interface EditorInterface {
|
||||
public GoodScrollView getVerticalScrollView();
|
||||
public String getFilePath();
|
||||
public PageSystem getPageSystem();
|
||||
public void updateTextSyntax();
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class EdittextPadding {
|
||||
|
||||
public static int getPaddingWithoutLineNumbers(Context context) {
|
||||
return (int) PixelDipConverter.convertDpToPixel(5, context);
|
||||
}
|
||||
|
||||
public static int getPaddingWithLineNumbers(Context context, float fontSize) {
|
||||
return (int) PixelDipConverter.convertDpToPixel(fontSize * 1.85f, context);
|
||||
}
|
||||
|
||||
public static int getPaddingTop(Context context) {
|
||||
return getPaddingWithoutLineNumbers(context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class EventBusEvents {
|
||||
public static class CannotOpenAFile {
|
||||
|
||||
}
|
||||
|
||||
public static class NewFileToOpen {
|
||||
|
||||
private final File file;
|
||||
private final String fileText;
|
||||
|
||||
public NewFileToOpen(File file) {
|
||||
this.file = file;
|
||||
this.fileText = "";
|
||||
}
|
||||
|
||||
public NewFileToOpen(String fileText) {
|
||||
this.file = new File("");
|
||||
this.fileText = fileText;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getFileText() {
|
||||
return fileText;
|
||||
}
|
||||
}
|
||||
|
||||
public static class AFileIsSelected {
|
||||
|
||||
private final String path;
|
||||
|
||||
public AFileIsSelected(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static class APreferenceValueWasChanged {
|
||||
private Type type;
|
||||
|
||||
public APreferenceValueWasChanged(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
FONT_SIZE, ENCODING, SYNTAX, WRAP_CONTENT, MONOSPACE, LINE_NUMERS, THEME_CHANGE, TEXT_SUGGESTIONS, AUTO_SAVE, READ_ONLY
|
||||
}
|
||||
}
|
||||
|
||||
public static class SaveAFile {
|
||||
}
|
||||
|
||||
public static class SavedAFile {
|
||||
private final String path;
|
||||
|
||||
public SavedAFile(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClosedAFile {
|
||||
}
|
||||
|
||||
public static class InvalideTheMenu {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import org.mozilla.universalchardet.UniversalDetector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class FileUtils {
|
||||
public static String getDetectedEncoding(File file) {
|
||||
InputStream is = null;
|
||||
String encoding = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
UniversalDetector detector = new UniversalDetector(null);
|
||||
byte[] buf = new byte[4096];
|
||||
int nread;
|
||||
while ((nread = is.read(buf)) > 0 && !detector.isDone()) {
|
||||
detector.handleData(buf, 0, nread);
|
||||
}
|
||||
detector.dataEnd();
|
||||
encoding = detector.getDetectedCharset();
|
||||
} catch (IOException e) {
|
||||
// nothing to do
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (encoding == null) {
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
}
|
||||
return encoding;
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
public class LineUtils {
|
||||
private boolean[] toCountLinesArray;
|
||||
private int[] realLines;
|
||||
|
||||
public boolean[] getToCountLinesArray() {
|
||||
return toCountLinesArray;
|
||||
}
|
||||
|
||||
public int[] getRealLines() {
|
||||
return realLines;
|
||||
}
|
||||
|
||||
public int getYAtLine(ScrollView scrollView, int lineCount, int line){
|
||||
return scrollView.getChildAt(0).getHeight() / lineCount * line;
|
||||
}
|
||||
|
||||
public int getFirstVisibleLine(ScrollView scrollView, int lineCount){
|
||||
return getFirstVisibleLine(scrollView, scrollView.getChildAt(0).getHeight(), lineCount);
|
||||
}
|
||||
|
||||
public int getFirstVisibleLine(ScrollView scrollView, int childHeight, int lineCount) throws ArithmeticException{
|
||||
int line = (scrollView.getScrollY() * lineCount) / childHeight;
|
||||
if (line < 0) line = 0;
|
||||
return line;
|
||||
}
|
||||
|
||||
public int getLastVisibleLine(int firstVisibleLine, int lineCount) {
|
||||
int line;
|
||||
line = firstVisibleLine + 150;
|
||||
if (line > lineCount) line = lineCount;
|
||||
return line;
|
||||
}
|
||||
|
||||
public void updateHasNewLineArray(int startingLine, int lineCount, Layout layout, String text) {
|
||||
|
||||
boolean[] hasNewLineArray = new boolean[lineCount];
|
||||
toCountLinesArray = new boolean[lineCount];
|
||||
realLines = new int[lineCount];
|
||||
|
||||
int i;
|
||||
|
||||
// for every line on the edittext
|
||||
for (i = 0; i < lineCount; i++) {
|
||||
// check if this line contains "\n"
|
||||
hasNewLineArray[i] = text.substring(layout.getLineStart(i), layout.getLineEnd(i)).endsWith("\n");
|
||||
// if true
|
||||
if (hasNewLineArray[i]) {
|
||||
int j = i - 1;
|
||||
while (j > -1 && !hasNewLineArray[j]) {
|
||||
j--;
|
||||
}
|
||||
toCountLinesArray[j + 1] = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int realLine = startingLine; // the first line is not 0, is 1. We start counting from 1
|
||||
|
||||
for (i = 0; i < toCountLinesArray.length; i++) {
|
||||
if (toCountLinesArray[i]) {
|
||||
realLine++;
|
||||
}
|
||||
realLines[i] = realLine;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line from the index of the letter in the text
|
||||
* @param index
|
||||
* @param lineCount
|
||||
* @param layout
|
||||
* @return
|
||||
*/
|
||||
public int getLineFromIndex(int index, int lineCount, Layout layout) {
|
||||
int line;
|
||||
int currentIndex = 0;
|
||||
|
||||
for (line = 0; line < lineCount; line++) {
|
||||
currentIndex += layout.getLineEnd(line) - layout.getLineStart(line);
|
||||
if (currentIndex > index) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
public int lastReadLine() {
|
||||
return realLines[realLines.length-1];
|
||||
}
|
||||
|
||||
public int fakeLineFromRealLine(int realLine) {
|
||||
int i;
|
||||
int fakeLine = 0;
|
||||
for(i = 0; i < realLines.length; i++) {
|
||||
if (realLine == realLines[i]){
|
||||
fakeLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fakeLine;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 920 Text Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 920 Text Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 920 Text Editor. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class LinuxShell {
|
||||
public static String getCmdPath(String path) {
|
||||
return path.replace(" ", "\\ ").replace("'", "\\'");
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回执行完<E8A18C><E5AE8C>?的结果
|
||||
*
|
||||
* @param cmd 命令内容
|
||||
* @return
|
||||
*/
|
||||
public static BufferedReader execute(String cmd) {
|
||||
BufferedReader reader = null; //errReader = null;
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec("su");
|
||||
DataOutputStream os = new DataOutputStream(process.getOutputStream());
|
||||
//os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n");
|
||||
//os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n");
|
||||
os.writeBytes(cmd + "\n");
|
||||
os.writeBytes("exit\n");
|
||||
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String err = (new BufferedReader(new InputStreamReader(process.getErrorStream()))).readLine();
|
||||
os.flush();
|
||||
|
||||
if (process.waitFor() != 0 || (!"".equals(err) && null != err)) {
|
||||
return null;
|
||||
}
|
||||
return reader;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*public static boolean isRoot()
|
||||
{
|
||||
boolean retval = false;
|
||||
Process suProcess;
|
||||
|
||||
try
|
||||
{
|
||||
suProcess = Runtime.getRuntime().exec("su");
|
||||
|
||||
DataOutputStream os =
|
||||
new DataOutputStream(suProcess.getOutputStream());
|
||||
DataInputStream osRes =
|
||||
new DataInputStream(suProcess.getInputStream());
|
||||
|
||||
if (null != os && null != osRes)
|
||||
{
|
||||
// Getting the id of the current user to check if this is root
|
||||
os.writeBytes("id\n");
|
||||
os.flush();
|
||||
|
||||
String currUid = osRes.readLine();
|
||||
boolean exitSu = false;
|
||||
if (null == currUid)
|
||||
{
|
||||
retval = false;
|
||||
exitSu = false;
|
||||
Log.e("ROOT", "Can't get root access or denied by user");
|
||||
}
|
||||
else if (true == currUid.contains("uid=0"))
|
||||
{
|
||||
retval = true;
|
||||
exitSu = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = false;
|
||||
exitSu = true;
|
||||
Log.e("ROOT", "Root access rejected: " + currUid);
|
||||
}
|
||||
|
||||
if (exitSu)
|
||||
{
|
||||
os.writeBytes("exit\n");
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Can't get root !
|
||||
// Probably broken pipe exception on trying to write to output
|
||||
// stream after su failed, meaning that the device is not rooted
|
||||
|
||||
retval = false;
|
||||
Log.d("ROOT", "Root access rejected [" +
|
||||
e.getClass().getName() + "] : " + e.getMessage());
|
||||
}
|
||||
|
||||
return retval;
|
||||
}*/
|
||||
}
|
||||
|
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
public class MimeTypes {
|
||||
public static final String[] MIME_TEXT = {
|
||||
"ajx", "am", "asa", "asc", "asp", "aspx", "awk", "bat", "c", "cdf", "cf", "cfg", "cfm", "cgi", "cnf", "conf", "cpp", "css", "csv", "ctl", "dat", "dhtml", "diz", "file", "forward", "grp", "h", "hpp", "hqx", "hta", "htaccess", "htc", "htm", "html", "htpasswd", "htt", "htx", "in", "inc", "info", "ini", "ink", "java", "js", "jsp", "key", "log", "logfile", "m3u", "m4", "m4a", "mak", "map", "model", "msg", "nfo", "nsi", "info", "old", "pas", "patch", "perl", "php", "php2", "php3", "php4", "php5", "php6", "phtml", "pix", "pl", "pm", "po", "pwd", "py", "qmail", "rb", "rbl", "rbw", "readme", "reg", "rss", "rtf", "ruby", "session", "setup", "sh", "shtm", "shtml", "sql", "ssh", "stm", "style", "svg", "tcl", "text", "threads", "tmpl", "tpl", "txt", "ubb", "vbs", "xhtml", "xml", "xrc", "xsl"
|
||||
};
|
||||
public static final String[] MIME_CODE = {
|
||||
"php", "js", "cs", "java", "py", "aspx", "cshtml", "vbhtml", "go", "c", "h", "cpp", "pl", "pm", "t", "pod",
|
||||
"m", "f", "for", "f90", "f95", "asp", "json", "wiki", "lua"
|
||||
};
|
||||
public static final String[] MIME_HTML = {
|
||||
"htm"
|
||||
};
|
||||
public static final String[] MIME_PICTURE = {
|
||||
"png", "jpeg", "jpg", "ico", "gif", "bmp", "tiff"
|
||||
};
|
||||
public static final String[] MIME_MUSIC = {
|
||||
"mp3", "avi", "flac", "mpga"
|
||||
};
|
||||
public static final String[] MIME_VIDEO = {
|
||||
"mp4", "mkv", "wmw"
|
||||
};
|
||||
public static final String[] MIME_ARCHIVE = {
|
||||
"zip", "tar", "gz", "bz2", "rar", "7z"
|
||||
};
|
||||
public static final String[] MIME_SQL = {
|
||||
"sql", "mdf", "ndf", "ldf"
|
||||
};
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class PageSystem {
|
||||
|
||||
public interface PageSystemInterface {
|
||||
void onPageChanged(int page);
|
||||
}
|
||||
|
||||
private List<String> pages;
|
||||
|
||||
private int[] startingLines;
|
||||
|
||||
private int currentPage = 0;
|
||||
|
||||
private PageSystemInterface pageSystemInterface;
|
||||
|
||||
public PageSystem(PageSystemInterface pageSystemInterface, String text) {
|
||||
this.pageSystemInterface = pageSystemInterface;
|
||||
pages = new LinkedList<>();
|
||||
|
||||
int i = 0;
|
||||
int charForPage = 15000;
|
||||
int maxLenghtInOnePage = 30000;
|
||||
int to;
|
||||
int indexOfReturn;
|
||||
int textLenght = text.length();
|
||||
if(textLenght > maxLenghtInOnePage) {
|
||||
while (i < textLenght) {
|
||||
to = i + charForPage;
|
||||
indexOfReturn = text.indexOf("\n", to);
|
||||
if (indexOfReturn > to) to = indexOfReturn;
|
||||
if (to > text.length()) to = text.length();
|
||||
pages.add(text.substring(i, to));
|
||||
i = to + 1;
|
||||
}
|
||||
|
||||
|
||||
if (i == 0)
|
||||
pages.add("");
|
||||
} else {
|
||||
pages.add(text);
|
||||
}
|
||||
|
||||
startingLines = new int[pages.size()];
|
||||
setStartingLines();
|
||||
}
|
||||
|
||||
public int getStartingLine() {
|
||||
return startingLines[currentPage];
|
||||
}
|
||||
|
||||
public String getCurrentPageText() {
|
||||
return pages.get(currentPage);
|
||||
}
|
||||
|
||||
public String getTextOfNextPages(boolean includeCurrent, int nOfPages) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int i;
|
||||
for(i = includeCurrent ? 0 : 1; i < nOfPages; i++){
|
||||
if(pages.size() > (currentPage + i)) {
|
||||
stringBuilder.append(pages.get(currentPage + 1));
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public void savePage(String currentText) {
|
||||
pages.set(currentPage, currentText);
|
||||
}
|
||||
|
||||
public void nextPage() {
|
||||
if(!canReadNextPage()) return;
|
||||
goToPage(currentPage + 1);
|
||||
}
|
||||
|
||||
public void prevPage() {
|
||||
if(!canReadPrevPage()) return;
|
||||
goToPage(currentPage - 1);
|
||||
}
|
||||
|
||||
public void goToPage(int page) {
|
||||
if(page >= pages.size()) page = pages.size() - 1;
|
||||
if(page < 0) page = 0;
|
||||
boolean shouldUpdateLines = page > currentPage && canReadNextPage();
|
||||
if(shouldUpdateLines) {
|
||||
String text = getCurrentPageText();
|
||||
int nOfNewLineNow = (text.length() - text.replace("\n", "").length()) + 1; // normally the last line is not counted so we have to add 1
|
||||
int nOfNewLineBefore = startingLines[currentPage+1] - startingLines[currentPage];
|
||||
int difference = nOfNewLineNow - nOfNewLineBefore;
|
||||
updateStartingLines(currentPage+1, difference);
|
||||
}
|
||||
currentPage = page;
|
||||
pageSystemInterface.onPageChanged(page);
|
||||
}
|
||||
|
||||
public void setStartingLines() {
|
||||
int i;
|
||||
int startingLine;
|
||||
int nOfNewLines;
|
||||
String text;
|
||||
startingLines[0] = 0;
|
||||
for(i = 1; i < pages.size(); i++) {
|
||||
text = pages.get(i-1);
|
||||
nOfNewLines = text.length() - text.replace("\n", "").length() + 1;
|
||||
startingLine = startingLines[i - 1] + nOfNewLines;
|
||||
startingLines[i] = startingLine;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateStartingLines(int fromPage, int difference) {
|
||||
if(difference == 0)
|
||||
return;
|
||||
int i;
|
||||
if(fromPage < 1) fromPage = 1;
|
||||
for(i = fromPage; i < pages.size(); i++) {
|
||||
startingLines[i] += difference;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxPage() {
|
||||
return pages.size() - 1;
|
||||
}
|
||||
|
||||
public int getCurrentPage() { return currentPage; }
|
||||
|
||||
public String getAllText(String currentPageText) {
|
||||
pages.set(currentPage, currentPageText);
|
||||
int i;
|
||||
StringBuilder allText = new StringBuilder();
|
||||
for(i = 0; i < pages.size(); i++) {
|
||||
allText.append(pages.get(i)).append("\n");
|
||||
}
|
||||
return allText.toString();
|
||||
}
|
||||
|
||||
public boolean canReadNextPage() {
|
||||
return currentPage < pages.size() - 1;
|
||||
}
|
||||
|
||||
public boolean canReadPrevPage() {
|
||||
return currentPage >= 1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
|
||||
import com.faizmalkani.floatingactionbutton.FloatingActionButton;
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
public class PageSystemButtons {
|
||||
|
||||
private static final int TIME_TO_SHOW_FABS = 2000;
|
||||
|
||||
public interface PageButtonsInterface {
|
||||
public void nextPageClicked();
|
||||
public void prevPageClicked();
|
||||
public void pageSystemButtonLongClicked();
|
||||
|
||||
public boolean canReadNextPage();
|
||||
public boolean canReadPrevPage();
|
||||
}
|
||||
|
||||
FloatingActionButton prev, next;
|
||||
PageButtonsInterface pageButtonsInterface;
|
||||
|
||||
public PageSystemButtons(Context context, final PageButtonsInterface pageButtonsInterface, FloatingActionButton prev, FloatingActionButton next) {
|
||||
this.prev = prev;
|
||||
this.next = next;
|
||||
this.pageButtonsInterface = pageButtonsInterface;
|
||||
|
||||
this.next.setColor(context.getResources().getColor(R.color.fab_light));
|
||||
this.next.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_right));
|
||||
|
||||
this.prev.setColor(context.getResources().getColor(R.color.fab_light));
|
||||
this.prev.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_left));
|
||||
|
||||
if(pageButtonsInterface.canReadNextPage())
|
||||
next.setVisibility(View.VISIBLE);
|
||||
|
||||
if(pageButtonsInterface.canReadPrevPage())
|
||||
prev.setVisibility(View.VISIBLE);
|
||||
|
||||
this.next.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
pageButtonsInterface.nextPageClicked();
|
||||
}
|
||||
});
|
||||
|
||||
this.next.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
pageButtonsInterface.pageSystemButtonLongClicked();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
this.prev.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
pageButtonsInterface.prevPageClicked();
|
||||
}
|
||||
});
|
||||
|
||||
this.prev.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
pageButtonsInterface.pageSystemButtonLongClicked();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PageSystemButtons.this.next.setVisibility(View.GONE);
|
||||
PageSystemButtons.this.prev.setVisibility(View.GONE);
|
||||
}
|
||||
};
|
||||
|
||||
public void updateVisibility(boolean autoHide) {
|
||||
|
||||
if(pageButtonsInterface.canReadNextPage())
|
||||
PageSystemButtons.this.next.setVisibility(View.VISIBLE);
|
||||
else
|
||||
PageSystemButtons.this.next.setVisibility(View.GONE);
|
||||
|
||||
if(pageButtonsInterface.canReadPrevPage())
|
||||
PageSystemButtons.this.prev.setVisibility(View.VISIBLE);
|
||||
else
|
||||
PageSystemButtons.this.prev.setVisibility(View.GONE);
|
||||
|
||||
/*if(pageButtonsInterface.hasNext())
|
||||
next.showFab();
|
||||
else
|
||||
next.hideFab();
|
||||
|
||||
if(pageButtonsInterface.hasPrev())
|
||||
prev.showFab();
|
||||
else
|
||||
prev.hideFab();*/
|
||||
|
||||
if(autoHide) {
|
||||
handler.removeCallbacks(runnable);
|
||||
handler.postDelayed(runnable, TIME_TO_SHOW_FABS);
|
||||
} else {
|
||||
handler.removeCallbacks(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Patterns {
|
||||
|
||||
/*
|
||||
public static final int COLOR_NUMBER = 0xffff6600;
|
||||
public static final int COLOR_KEYWORD = 0xff2f6f9f;
|
||||
public static final int COLOR_ATTR = 0xff4f9fcf;
|
||||
public static final int COLOR_ATTR_VALUE = 0xffd44950;
|
||||
public static final int COLOR_STRING = 0xffd44950;
|
||||
public static final int COLOR_COMMENT = 0xff999999;
|
||||
*/
|
||||
|
||||
// Strings
|
||||
public static final Pattern GENERAL_STRINGS = Pattern.compile("\"(.*?)\"|'(.*?)'");
|
||||
|
||||
public static final Pattern HTML_OPEN_TAGS = Pattern.compile(
|
||||
"<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>");
|
||||
public static final Pattern HTML_CLOSE_TAGS = Pattern.compile(
|
||||
"</([A-Za-z][A-Za-z0-9]*)\\b[^>]*>");
|
||||
public static final Pattern HTML_ATTRS = Pattern.compile(
|
||||
"(\\S+)=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?");
|
||||
|
||||
//static final Pattern CSS_STYLE_NAME= Pattern.compile(
|
||||
// "[ \\t\\n\\r\\f](.+?)\\{([^\\)]+)\\}");
|
||||
public static final Pattern CSS_ATTRS = Pattern.compile(
|
||||
"(.+?):(.+?);");
|
||||
public static final Pattern CSS_ATTR_VALUE = Pattern.compile(
|
||||
":[ \t](.+?);");
|
||||
|
||||
public static final Pattern NUMBERS = Pattern.compile(
|
||||
"\\b(\\d*[.]?\\d+)\\b");
|
||||
public static final Pattern CSS_NUMBERS = Pattern.compile(
|
||||
"/^auto$|^[+-]?[0-9]+\\.?([0-9]+)?(px|em|ex|%|in|cm|mm|pt|pc)?$/ig");
|
||||
public static final Pattern SYMBOLS = Pattern.compile(
|
||||
"(!|,|\\(|\\)|\\+|\\-|\\*|<|>|=|\\.|\\?|;|\\{|\\}|\\[|\\])");
|
||||
public static final Pattern GENERAL_KEYWORDS = Pattern.compile(
|
||||
"\\b(alignas|alignof|and|and_eq|asm|auto|bitand|bitorbool|break|case|catch|char|"
|
||||
+ "char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype"
|
||||
+ "|default|delete|do|double|dynamic_cast|echo|else|enum|explicit|export|extern|"
|
||||
+ "false|float|for|friend|function|goto|if|inline|int|mutable|namespace|new|noexcept|"
|
||||
+ "not|not_eq|null|nullptr|operator|or|or_eq|private|protected|public|register|"
|
||||
+ "reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast"
|
||||
+ "|struct|switch|template|this|thread_local|throw|true|try|typedef|typeid|typename|undefined"
|
||||
+ "|union|unsigned|using|var|virtual|void|volatile|wchar_t|while|xor|xor_eq|)\\b", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern PY_KEYWORDS = Pattern.compile(
|
||||
"\\b(int|float|long|complex|str|unicode|list|tuple|bytearray|buffer|xrange|set|frozenset|dict|bool)|(True|False|None|self|NotImplemented|Ellipsis|__debug__|__file__)|(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)|(ArithmeticError|AssertionError|AttributeError|BaseException|DeprecationWarning|EnvironmentError|EOFError|Exception|FloatingPointError|FutureWarning|GeneratorExit|IOError|ImportError|ImportWarning|IndexError|KeyError|KeyboardInterrupt|LookupError|MemoryError|NameError|NotImplementedError|OSError|OverflowError|PendingDeprecationWarning|ReferenceError|RuntimeError|RuntimeWarning|StandardError|StopIteration|SyntaxError|SyntaxWarning|SystemError|SystemExit|TypeError|UnboundLocalError|UserWarning|UnicodeError|UnicodeWarning|UnicodeEncodeError|UnicodeDecodeError|UnicodeTranslateError|ValueError|Warning|WindowsError|ZeroDivisionError)\\b", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static final Pattern LUA_KEYWORDS = Pattern.compile(
|
||||
"@[A-Za-z0-9_\\.]*|\\b(local|global|boolean|number|userdata)\\b|\\b(true|false|nil)\\b|\\b(return|then|while|and|break|do|else|elseif|end|for|function|if|in|not|or|repeat|until|thread|table)\\b" +
|
||||
"|(?i)\\b(editsetText|editText|inkey|touch|system.exit|system.expCall|system.getAppPath|system.getCardMnt|system.getSec|system.impCallActionSend|system.impCallActionView|system.setrun|system.setScreen|system.version|El_Psy_Congroo|canvas.drawCircle|canvas.drawCls|canvas.drawLine|canvas.drawRect|canvas.getBmpSize|canvas.getColor|canvas.getg|canvas.getviewSize|canvas.loadBmp|canvas.putCircle|canvas.putCls|canvas.putflush|canvas.putg|canvas.putLine|canvas.putRect|canvas.putrotg|canvas.putWork|canvas.saveBmp|canvas.setMainBmp|canvas.setWorkBmp|canvas.workCls|canvas.workflush|color|canvas.drawText|canvas.drawTextBox|canvas.drawTextCenter|canvas.drawTextRotate|canvas.putText|canvas.putTextBox|canvas.putTextRotate|http.addHeader|http.addParam|http.clrHeader|http.clrParam|http.get|http.post|http.setContentType|http.setPostFile|http.status|dialog|item.add|item.check|item.clear|item.list|item.radio|toast|sensor.getAccel|sensor.setdevAccel|sensor.setdevMagnet|sensor.setdevOrient|sensor.getGdirection|sensor.getMagnet|sensor.getOrient|sound.beep|sound.isPlay|sound.pause|sound.restart|sound.setSoundFile|sound.start|sound.stop|zip.addFile|zip.exec|zip.status|sock.close|sock.connectOpen|sock.getAddress|sock.listenOpen|sock.recv|sock.send|sprite.clear|sprite.define|sprite.init|sprite.move|sprite.put)\\b" +
|
||||
"|(?i)\\b(assert|collectgarbage|coroutine.create|coroutine.resume|coroutine.running|coroutine.status|coroutine.wrap|coroutine.yield|debug.debug|debug.getfenv|debug.gethook|debug.getinfo|debug.getlocal|debug.getmetatable|debug.getregistry|debug.getupvalue|debug.setfenv|debug.sethook|debug.setlocal|debug.setmetatable|debug.setupvalue|debug.traceback|dofile|error|file:close|file:flush|file:lines|file:read|file:seek|file:setvbuf|file:write|getfenv|getmetatable|io.close|io.flush|io.input|io.lines|io.open|io.output|io.popen|io.read|io.tmpfile|io.type|io.write|ipairs|load|loadfile|loadstring|math.abs|math.acos|math.asin|math.atan2|math.atan|math.ceil|math.cosh|math.cos|math.deg|math.exp|math.floor|math.fmod|math.frexp|math.ldexp|math.log10|math.log|math.max|math.min|math.modf|math.pow|math.rad|math.random|math.randomseed|math.sinh|math.sin|math.sqrt|math.tanh|math.tan|module|next|os.clock|os.date|os.difftime|os.execute|os.exit|os.getenv|os.remove|os.rename|os.setlocale|os.time|os.tmpname|package.cpath|package.loaded|package.loadlib|package.path|package.preload|package.seeal|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|string.byte|string.char|string.dump|string.find|string.format|string.gmatch|string.gsub|string.len|string.lower|string.match|string.rep|string.reverse|string.sub|string.upper|table.concat|table.insert|table.maxn|table.remove|table.sort|tonumber|tostring|type|unpack|xpcall)\\b"
|
||||
);
|
||||
|
||||
public static final Pattern PHP_VARIABLES = Pattern.compile("\\$\\s*(\\w+)");
|
||||
// Comments
|
||||
public static final Pattern XML_COMMENTS = Pattern.compile("(?s)<!--.*?-->");
|
||||
public static final Pattern GENERAL_COMMENTS = Pattern.compile(
|
||||
"/\\*(?:.|[\\n\\r])*?\\*/|//.*|#.*");
|
||||
public static final Pattern GENERAL_COMMENTS_NO_SLASH = Pattern.compile(
|
||||
"/\\*(?:.|[\\n\\r])*?\\*/|#.*");
|
||||
public static final Pattern SQL_KEYWORDS = Pattern.compile(
|
||||
"\\b(ADD|EXCEPT|PERCENT|ALL|EXEC|PLAN|ALTER|EXECUTE|PRECISION|AND|EXISTS|PRIMARY|ANY|EXIT|PRINT|AS|FETCH|PROC|ASC|FILE|PROCEDURE|AUTHORIZATION|FILLFACTOR|PUBLIC|BACKUP|FOR|RAISERROR|BEGIN|FOREIGN|READ|BETWEEN|FREETEXT|READTEXT|BREAK|FREETEXTTABLE|RECONFIGURE|BROWSE|FROM|REFERENCES|BULK|FULL|REPLICATION|BY|FUNCTION|RESTORE|CASCADE|GOTO|RESTRICT|CASE|GRANT|RETURN|CHECK|GROUP|REVOKE|CHECKPOINT|HAVING|RIGHT|CLOSE|HOLDLOCK|ROLLBACK|CLUSTERED|IDENTITY|ROWCOUNT|COALESCE|IDENTITY_INSERT|ROWGUIDCOL|COLLATE|IDENTITYCOL|RULE|COLUMN|IF|SAVE|COMMIT|IN|SCHEMA|COMPUTE|INDEX|SELECT|CONSTRAINT|INNER|SESSION_USER|CONTAINS|INSERT|SET|CONTAINSTABLE|INTERSECT|SETUSER|CONTINUE|INTO|SHUTDOWN|CONVERT|IS|SOME|CREATE|JOIN|STATISTICS|CROSS|KEY|SYSTEM_USER|CURRENT|KILL|TABLE|CURRENT_DATE|LEFT|TEXTSIZE|CURRENT_TIME|LIKE|THEN|CURRENT_TIMESTAMP|LINENO|TO|CURRENT_USER|LOAD|TOP|CURSOR|NATIONAL|TRAN|DATABASE|NOCHECK|TRANSACTION|DBCC|NONCLUSTERED|TRIGGER|DEALLOCATE|NOT|TRUNCATE|DECLARE|NULL|TSEQUAL|DEFAULT|NULLIF|UNION|DELETE|OF|UNIQUE|DENY|OFF|UPDATE|DESC|OFFSETS|UPDATETEXT|DISK|ON|USE|DISTINCT|OPEN|USER|DISTRIBUTED|OPENDATASOURCE|VALUES|DOUBLE|OPENQUERY|VARYING|DROP|OPENROWSET|VIEW|DUMMY|OPENXML|WAITFOR|DUMP|OPTION|WHEN|ELSE|OR|WHERE|END|ORDER|WHILE|ERRLVL|OUTER|WITH|ESCAPE|OVER|WRITETEXT)\\b", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
||||
public static final Pattern LINK = android.util.Patterns.WEB_URL;
|
||||
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
public final class PixelDipConverter {
|
||||
|
||||
private PixelDipConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method convets dp unit to equivalent device specific value in pixels.
|
||||
*
|
||||
* @param dp A value in dp(Device independent pixels) unit. Which we need to convert into pixels
|
||||
* @param context Context to get resources and device specific display metrics
|
||||
* @return A float value to represent Pixels equivalent to dp according to device
|
||||
*/
|
||||
public static float convertDpToPixel(final float dp, final Context context) {
|
||||
final Resources resources = context.getResources();
|
||||
final DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return dp * metrics.densityDpi / 160f;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts device specific pixels to device independent pixels.
|
||||
*
|
||||
* @param px A value in px (pixels) unit. Which we need to convert into db
|
||||
* @param context Context to get resources and device specific display metrics
|
||||
* @return A float value to represent db equivalent to px value
|
||||
*/
|
||||
public static float convertPixelsToDp(final float px, final Context context) {
|
||||
final Resources resources = context.getResources();
|
||||
final DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return px / (metrics.densityDpi / 160f);
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class ProCheckUtils {
|
||||
public static boolean isPro(Context context) {
|
||||
|
||||
String packageName = context.getPackageName();
|
||||
|
||||
if(Constants.FOR_AMAZON)
|
||||
return true;
|
||||
else if(packageName.equals("com.maskyn.fileeditorpro"))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 920 Text Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 920 Text Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with 920 Text Editor. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.Toolbox;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class RootUtils {
|
||||
|
||||
public static void writeFile(Context context, String path, String text, String encoding, boolean isRoot) throws Exception {
|
||||
File file = new File(path);
|
||||
if (!file.canWrite() && isRoot) {
|
||||
File appFolder = context.getFilesDir();
|
||||
File tempFile = new File(appFolder, "temp.root.file");
|
||||
if (!tempFile.exists())
|
||||
tempFile.createNewFile();
|
||||
FileUtils.write(tempFile, text, encoding);
|
||||
Shell shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
String mount = tb.getFilePermissions(path);
|
||||
tb.copyFile(tempFile.getAbsolutePath(), path, true, false);
|
||||
tb.setFilePermissions(path, mount);
|
||||
tempFile.delete();
|
||||
} else {
|
||||
FileUtils.write(file,
|
||||
text,
|
||||
encoding);
|
||||
}
|
||||
}
|
||||
|
||||
public static ArrayList<File> getFileList(String path, boolean runAtRoot) {
|
||||
ArrayList<File> filesList = new ArrayList<File>();
|
||||
if (runAtRoot == false) {
|
||||
File base = new File(path);
|
||||
File[] files = base.listFiles();
|
||||
if (files == null)
|
||||
return null;
|
||||
Collections.addAll(filesList, files);
|
||||
} else {
|
||||
BufferedReader reader = null; //errReader = null;
|
||||
try {
|
||||
|
||||
reader = LinuxShell.execute("IFS='\n';CURDIR='" + LinuxShell.getCmdPath(path) + "';for i in `ls $CURDIR`; do if [ -d $CURDIR/$i ]; then echo \"d $CURDIR/$i\";else echo \"f $CURDIR/$i\"; fi; done");
|
||||
if (reader == null)
|
||||
return null;
|
||||
|
||||
File f;
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
f = new File(line.substring(2));
|
||||
filesList.add(f);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return filesList;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
import sharedcode.turboeditor.R;
|
||||
|
||||
import org.sufficientlysecure.rootcommands.Shell;
|
||||
import org.sufficientlysecure.rootcommands.Toolbox;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
|
||||
public class SaveFileTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private final Context context;
|
||||
private final String filePath;
|
||||
private final String text;
|
||||
private final String encoding;
|
||||
private File file;
|
||||
private String message;
|
||||
private String positiveMessage;
|
||||
|
||||
public SaveFileTask(Context context, String filePath, String text, String encoding) {
|
||||
this.context = context;
|
||||
this.filePath = filePath;
|
||||
this.text = text;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
file = new File(filePath);
|
||||
positiveMessage = String.format(context.getString(R.string.file_saved_with_success), file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected Void doInBackground(final Void... voids) {
|
||||
|
||||
try {
|
||||
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
boolean isRoot = false;
|
||||
if (!file.canWrite()) {
|
||||
try {
|
||||
Shell shell = null;
|
||||
shell = Shell.startRootShell();
|
||||
Toolbox tb = new Toolbox(shell);
|
||||
isRoot = tb.isRootAccessGiven();
|
||||
} catch (IOException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
isRoot = false;
|
||||
}
|
||||
}
|
||||
|
||||
RootUtils.writeFile(context, file.getAbsolutePath(), text, encoding, isRoot);
|
||||
|
||||
message = positiveMessage;
|
||||
} catch (Exception e) {
|
||||
message = e.getMessage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(final Void aVoid) {
|
||||
super.onPostExecute(aVoid);
|
||||
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
|
||||
if(message.equals(positiveMessage))
|
||||
EventBus.getDefault().post(new EventBusEvents.SavedAFile(filePath));
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Vlad Mihalachi
|
||||
*
|
||||
* This file is part of Turbo Editor.
|
||||
*
|
||||
* Turbo Editor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Turbo Editor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sharedcode.turboeditor.util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class SearchResult {
|
||||
public LinkedList<Integer> foundIndex;
|
||||
public int textLength;
|
||||
public boolean isReplace;
|
||||
public String textToReplace;
|
||||
public int index;
|
||||
|
||||
public SearchResult(LinkedList<Integer> foundIndex, int textLength, boolean isReplace, String textToReplace) {
|
||||
this.foundIndex = foundIndex;
|
||||
this.textLength = textLength;
|
||||
this.isReplace = isReplace;
|
||||
this.textToReplace = textToReplace;
|
||||
}
|
||||
|
||||
public void doneReplace() {
|
||||
foundIndex.remove(index);
|
||||
int i;
|
||||
for(i = index; i < foundIndex.size(); i++) {
|
||||
foundIndex.set(i, foundIndex.get(i) + textToReplace.length() - textLength);
|
||||
}
|
||||
index--; // an element was removed so we decrease the index
|
||||
}
|
||||
|
||||
public int numberOfResults() {
|
||||
return foundIndex.size();
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user