55 Commits

Author SHA1 Message Date
46ad99aa46 when shift key is pressed delegate to super implementation so shift key is handled correctly, for certain hardware keyboards this is required so the editor is not stuck in shift mode 2016-09-25 02:09:17 -05:00
9408fba5dd Merge remote-tracking branch 'mitchellgordon95/patch-1' 2016-09-25 01:18:04 -05:00
ebce0bd69b Merge remote-tracking branch 'ldemianiuk/replace-fix' 2016-09-25 01:17:02 -05:00
ce2c2d8b0f Removing generated build stuff 2016-09-25 01:13:45 -05:00
0de5a82cdf Merge remote-tracking branch 'Zerglrisk/master' 2016-09-25 01:12:36 -05:00
69f8f2b0c7 Update gradle wrapper to gradle 2.14 2016-09-25 01:00:52 -05:00
245b9f4230 Pin android gradle plugin to 2.1.2 2016-09-25 00:41:06 -05:00
3db0519ec6 File open Bug Fix & Add Page System's Split By Line Mode 2016-06-21 09:56:29 +09:00
9b7de55f55 Fix Folder open if no file 2016-06-20 22:03:12 +09:00
66a86dc513 Add Folder Open 2016-06-20 18:28:39 +09:00
38123fd510 Support opening .org files
Org (organization) files are common among emacs developers. If you're not familiar with it, it's kind of like markdown. It would be nice to be able to automatically open these.
2016-04-19 11:01:56 -07:00
lde
481fe94eb9 fix replace all 2016-02-01 23:25:59 +01:00
6a965ca560 Update README.md 2015-06-25 15:42:20 +02:00
a1e82b65d4 Version 1.19 2015-03-26 21:26:24 +01:00
e399a217e5 Version 1.18.4 2015-03-25 14:47:22 +01:00
68ad318388 Version 1.18.3 2015-03-23 15:31:39 +01:00
1f3ad1ab12 Version 1.17
Added a markdown viewer and fixed a bug with Copy/Paste/Cut actions
2015-03-15 18:45:18 +01:00
2414192638 Merge remote-tracking branch 'origin/master'
Conflicts:
	build/intermediates/dex-cache/cache.xml
2015-03-15 17:38:46 +01:00
1cd94a5310 Small changes 2015-03-15 17:38:03 +01:00
ce4d1756fb Merge pull request #70 from DF1E/master
remove generated files from repo
2015-03-15 17:36:08 +01:00
3234597537 remove old generated files 2015-03-15 14:07:20 +01:00
5f77e6d7b5 remove generated files from git repo 2015-03-15 14:01:29 +01:00
60395106e5 Version 1.16: Accessory View and Black theme
Accessory View and Black theme
2015-03-15 09:26:28 +01:00
3ee11e0bfb Updated support library 2015-03-14 13:21:11 +01:00
b71d27aeaa Commit 2015-03-14 13:12:23 +01:00
05e4157bed Version 1.15
∙ It is a "beta" release, feel free to send you feedback on github.com!
∙ Added Save As! (and rename option)
∙ Added Replace All!
∙ The default encoding now is UTF-16
∙ Removed donations items, if you want to donate please buy the pro
version!
∙ Minor bug fixes
2015-03-14 13:11:56 +01:00
89673216e8 Small changes 2014-12-11 18:58:22 +01:00
7a86540c60 First mac commit 2014-12-11 18:28:54 +01:00
b359950e34 Fixes 2014-10-31 16:42:35 +01:00
614274c7d6 Happy Halloween! 2014-10-30 20:17:33 +01:00
78407c292a New material things 2014-10-25 14:35:31 +02:00
4d26fa7f3d Updated translations from crowdin 2014-10-18 15:47:54 +02:00
4b16119b53 Version 1.13 2014-10-18 15:20:23 +02:00
bf4dcd51b8 Merge pull request #41 from RyDroid/gitignore
Adding some rules to .gitignore
2014-10-18 15:02:54 +02:00
e52a3cb9f5 Merge pull request #42 from RyDroid/mime-types
Adding and reorganizing a little mime types
2014-10-18 15:02:27 +02:00
5702a6c24f Adding and reorganizing a little mime types 2014-10-18 01:29:07 +02:00
ffcd50616e Adding some rules to .gitignore 2014-10-18 01:25:09 +02:00
46842a5343 Version 1.12 2014-10-12 18:11:19 +02:00
df5a302129 Some fixes 2014-10-07 18:55:18 +02:00
04faa104ed Merge pull request #30 from DF1E/master
update LinuxShell from SimpleExplorer
2014-10-02 19:03:37 +02:00
a20f93f0f6 remove old info from LinuxShell 2014-10-02 18:33:44 +02:00
564e55385f update LinuxShell from SimpleExplorer
https://github.com/DF1E/SimpleExplorer/blob/master/src/com/dnielfe/manager/commands/RootCommands.java
2014-10-02 18:25:12 +02:00
2c62965a02 Version 1.11 2014-09-30 20:01:43 +02:00
eaab21069b Some minor but major changes
New advance settings screen, new setting to ignore the back button, new
behaviour of the left and right panels, now the app highlightes all the
visible text
2014-09-28 10:55:13 +02:00
2dbab5220a fixed a visual issue 2014-09-27 17:48:57 +02:00
520c4c53c0 Readded the jar, and fixed a keyboard issue 2014-09-27 15:20:05 +02:00
2fefb3963c Update README.md 2014-09-27 14:49:52 +02:00
e0eb01168e Update README.md 2014-09-26 12:53:13 +02:00
30294f72b5 Update README.md 2014-09-26 12:07:42 +02:00
ed621d369e Update README.md 2014-09-26 12:06:32 +02:00
6507331360 Update README.md 2014-09-26 11:40:00 +02:00
ec4742149b Update README.md 2014-09-26 11:36:05 +02:00
97dd211dc2 Removed the jar "juniversalchardet" 2014-09-26 11:24:19 +02:00
fc67e5930e Added the "pro" module
The pro version doesn't have ads so it doesn't require crashlytics and
play-services
2014-09-25 20:00:25 +02:00
44d4a2828b Small change 2014-09-25 14:31:27 +02:00
481 changed files with 29932 additions and 22866 deletions

124
.gitignore vendored
View File

@ -1,61 +1,63 @@
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
app-pro/
# Built application files
/*/build/
# Crashlytics configuations
com_crashlytics_export_strings.xml
crashlytics-build.properties
crashlytics.properties
# Local configuration file (sdk path, etc)
local.properties
# Gradle generated files
.gradle/
# Signing files
.signing/
# User-specific configurations
.idea/libraries/
.idea/workspace.xml
.idea/tasks.xml
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
*.iml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
doc/
/build
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Built application files
/*/build/
# Crashlytics configuations
com_crashlytics_export_strings.xml
crashlytics-build.properties
crashlytics.properties
# Local configuration file (sdk path, etc)
local.properties
# Gradle generated files
.gradle/
# Signing files
.signing/
# User-specific configurations
.idea/libraries/
.idea/workspace.xml
.idea/tasks.xml
.idea/.name
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/encodings.xml
.idea/misc.xml
.idea/modules.xml
.idea/scopes/scope_settings.xml
.idea/vcs.xml
*.iml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
*~
#*#

View File

@ -1,9 +0,0 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright (C) 2014 Vlad Mihalachi&#10;&#10;This file is part of Turbo Editor.&#10;&#10;Turbo Editor is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;Turbo Editor is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;http://www.gnu.org/licenses/&gt;." />
<option name="keyword" value="Copyright" />
<option name="allowReplaceKeyword" value="Copyright" />
<option name="myName" value="Copyright Vlad Mihalachi" />
<option name="myLocal" value="true" />
</copyright>
</component>

8
.idea/gradle.xml generated
View File

@ -3,8 +3,9 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="C:\Android\android-studio\gradle\gradle-2.10" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
@ -12,12 +13,11 @@
<option value="$PROJECT_DIR$/app-pro" />
<option value="$PROJECT_DIR$/libraries" />
<option value="$PROJECT_DIR$/libraries/FloatingActionButton" />
<option value="$PROJECT_DIR$/libraries/RootCommands" />
<option value="$PROJECT_DIR$/libraries/sharedCode" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
</project>

View File

@ -1,9 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="AndroidLintMissingQuantity" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AndroidLintMissingTranslation" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AndroidLintUnusedQuantity" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

1354
LICENSE

File diff suppressed because it is too large Load Diff

128
README.md
View File

@ -1,66 +1,62 @@
# Turbo Editor
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/turbo-client/localized.png)](https://crowdin.com/project/turbo-client)
Simple, powerful and Open Source file editor for Android licensed under the GPLv3 license.
### Contribute
You can contribute to this project in many ways:
* Browse our issues, comment on proposals, report bugs.
* Clone the balanced-dashboard repo, make some changes according to our
development guidelines and issue a pull-request with your changes.
* Help to translate the application on [Crowdin][crowdin]
* [Donate][donate]
------
### Development guidelines
1. Fork it (`git clone git://github.com/vmihalachi/turbo-editor.git`)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Write your code
4. Commit your changes (`git commit -am 'Add some feature'`)
5. Push to the branch (`git push origin my-new-feature`)
6. Create new [pull request](https://help.github.com/articles/using-pull-requests)
------
###Donate
* Make a [Paypal][donate paypal] donation
* Flattr this project [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=vmihalachi&url=https://raw.github.com/vmihalachi/turbo-editor&title=Turbo Editor&language=&tags=github&category=software)
------
###Download
* From the [Play Store][download playstore]
* Want to try beta releases? Be a part of the Google Plus [Community][community googleplus]!
------
### Developer
[Vlad Mihalachi][developer site]
------
### A special thanks to..
* [Dumitru Grubii][contributor dumitru grubii] for the icon
* All the translators
* You?
------
### License
Turbo Client is made available under the terms of the [GPLv3][gplv3].
See the [LICENSE][license] file that accompanies this distribution for the full text of the license.
[gplv3]: http://www.gnu.org/licenses/gpl.html
[license]: https://github.com/vmihalachi/turbo-editor/LICENSE
[donate]: https://github.com/vmihalachi/turbo-editor#donate
[donate paypal]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PUQXSX6MTXHZ2
[community googleplus]: https://plus.google.com/u/0/communities/111974095419108178946
[download playstore]: https://play.google.com/store/apps/details?id=com.maskyn.fileeditor
[crowdin]: https://crowdin.net/project/turbo-client
[developer site]: http://vmihalachi.com/
[crowdin]: https://crowdin.net/project/turbo-client
[contributor dumitru grubii]: https://twitter.com/DumitruGrubii
[project issues]: https://github.com/vmihalachi/turbo-editor/issues
[project wiki]: https://github.com/vmihalachi/turbo-editor/wiki
# Turbo Editor
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/turbo-client/localized.png)](https://crowdin.com/project/turbo-client)
Simple, powerful and Open Source text editor for Android licensed under the GPLv3 license.
### Download
[![Play Store](http://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](http://play.google.com/store/apps/details?id=com.maskyn.fileeditorpro)
[![F-Droid](https://lh5.googleusercontent.com/-zezQqsBye0c/VCUpPVjcKEI/AAAAAAAAAIQ/HbcG5f1qMIw/w129-h45-no/getitonfdroid.png)](https://f-droid.org/repository/browse/?fdid=com.maskyn.fileeditorpro)
### Contribute
You can contribute to this project in many ways:
* Browse our issues, comment on proposals, report bugs.
* Help to translate the application on [Crowdin][crowdin]
* Be a part of the Google Plus [Community][community googleplus]
* Discuss with us on [XDA thread][xda thread]
* Help to maintain this project active and [Donate][donate]
------
###Donate
[![Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=26VWS2TSAMUJA)
[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=vmihalachi&url=https://github.com/vmihalachi/turbo-editor&title=Turbo Editor&language=java&tags=github&category=software)
------
###Images
![](https://lh3.googleusercontent.com/-0GHukwGQPW4/VCUpEhKnZCI/AAAAAAAAAH4/cclI70K79_Q/w347-h520-no/PhoneCustom_7.png)![](https://lh3.googleusercontent.com/-OvazluFl_QQ/VCUo9DAje9I/AAAAAAAAAHQ/i7n1uCpU1hE/w347-h520-no/PhoneCustom_1.png)![](https://lh4.googleusercontent.com/-zh4CYdQPecg/VCUpD3QXpAI/AAAAAAAAAHw/ulL5-V0iJUw/w347-h520-no/PhoneCustom_6.png)![](https://lh4.googleusercontent.com/-LT3k4z9JHo8/VCUo_0jnZRI/AAAAAAAAAHg/Npk9UlkXIV8/w347-h520-no/PhoneCustom_4.png)![](https://lh5.googleusercontent.com/-hXvsf-WYvBs/VCUo9sYfR-I/AAAAAAAAAHY/TTfAgiV_7ko/w347-h520-no/PhoneCustom_3.png)![](https://lh6.googleusercontent.com/-Qib82pK6mZU/VCUpAgYmUdI/AAAAAAAAAHo/zoPVmwcatbQ/w347-h520-no/PhoneCustom_5.png)![](https://lh5.googleusercontent.com/-SERL7X-JHuc/VCax0QSlCGI/AAAAAAAAAJA/hu8dvbvJGBM/w375-h563-no/PhoneCustom_2.png)
------
### Developer
[Vlad Mihalachi][developer site]
------
### A special thanks to..
* [Dumitru Grubii][contributor dumitru grubii] for the icon
* All the translators
* [Howard C. Shaw III][contributor howard] for the Word count feature
* You?
------
### License
Turbo Client is made available under the terms of the [GPLv3][gplv3].
See the [LICENSE][license] file that accompanies this distribution for the full text of the license.
[gplv3]: http://www.gnu.org/licenses/gpl.html
[license]: https://github.com/vmihalachi/turbo-editor/LICENSE
[donate]: https://github.com/vmihalachi/turbo-editor#donate
[donate paypal]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PUQXSX6MTXHZ2
[xda thread]: http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016
[community googleplus]: https://plus.google.com/u/0/communities/111974095419108178946
[crowdin]: https://crowdin.net/project/turbo-client
[developer site]: http://vmihalachi.com/
[crowdin]: https://crowdin.net/project/turbo-client
[contributor dumitru grubii]: https://twitter.com/DumitruGrubii
[contributor howard]: https://plus.google.com/+HowardCShawIII
[project issues]: https://github.com/vmihalachi/turbo-editor/issues
[project wiki]: https://github.com/vmihalachi/turbo-editor/wiki

2
app-pro/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build
manifest-merger-release-report.txt

73
app-pro/build.gradle Normal file
View File

@ -0,0 +1,73 @@
/*
* 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/>.
*/
import java.util.regex.Pattern
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion '22.0.1'
defaultConfig {
applicationId "com.maskyn.fileeditorpro"
minSdkVersion 11
targetSdkVersion 22
versionCode 47
versionName "1.19"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':libraries:sharedCode')
}
task incrementVersionCode << {
println(":incrementVersionCode - Incrementing Version Code...")
def buildGradleFile = file("build.gradle")
def patternVersionCode = Pattern.compile("versionCode (\\d+)")
def buildGradleFileText = buildGradleFile.getText()
def matcherVersionCode = patternVersionCode.matcher(buildGradleFileText)
matcherVersionCode.find()
def mVersionCode = Integer.parseInt(matcherVersionCode.group(1))
def mNextVersionCode = mVersionCode + 1
def manifestContent = matcherVersionCode.replaceAll("versionCode " + mNextVersionCode)
println(":incrementVersionCode - current versionCode=" + mVersionCode);
println(":incrementVersionCode - next versionCode=" + mNextVersionCode);
buildGradleFile.write(manifestContent)
}

17
app-pro/proguard-rules.pro vendored Normal file
View 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 *;
#}

View File

@ -1,37 +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.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);
}
}
/*
* 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 com.maskyn.fileeditorpro;
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);
}
}

View File

@ -0,0 +1,160 @@
<!--
~ 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="com.maskyn.fileeditorpro"
android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/nome_app_turbo_editor"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:supportsRtl="true"
android:name="sharedcode.turboeditor.application.MyApp"
>
<!-- android:alwaysRetainTaskState="true" -->
<activity
android:name=".HomeActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:launchMode="singleTop"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:theme="@style/AppThemeEditorDark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.txt" />
<data android:pathPattern=".*\\.html" />
<data android:pathPattern=".*\\.xml" />
<data android:pathPattern=".*\\.css" />
<data android:pathPattern=".*\\.js" />
<data android:pathPattern=".*\\.md"/>
<data android:pathPattern=".*\\.markdown"/>
<data android:pathPattern=".*\\.php" />
<data android:pathPattern=".*\\.py" />
<data android:pathPattern=".*\\.script" />
<data android:pathPattern=".*\\.cs" />
<data android:pathPattern=".*\\.java" />
<data android:pathPattern=".*\\.rb" />
<data android:pathPattern=".*\\.aspx" />
<data android:pathPattern=".*\\.cshtml" />
<data android:pathPattern=".*\\.vbhtml" />
<data android:pathPattern=".*\\.go" />
<data android:pathPattern=".*\\.c" />
<data android:pathPattern=".*\\.h" />
<data android:pathPattern=".*\\.cc" />
<data android:pathPattern=".*\\.cpp" />
<data android:pathPattern=".*\\.hh" />
<data android:pathPattern=".*\\.hpp" />
<data android:pathPattern=".*\\.pl" />
<data android:pathPattern=".*\\.pm" />
<data android:pathPattern=".*\\.t" />
<data android:pathPattern=".*\\.pod" />
<data android:pathPattern=".*\\.m" />
<data android:pathPattern=".*\\.f" />
<data android:pathPattern=".*\\.for" />
<data android:pathPattern=".*\\.f90" />
<data android:pathPattern=".*\\.f95" />
<data android:pathPattern=".*\\.asp" />
<data android:pathPattern=".*\\.json" />
<data android:pathPattern=".*\\.wiki" />
<data android:pathPattern=".*\\.lua" />
<data android:pathPattern=".*\\.r" />
<data android:pathPattern=".*\\.key" />
<data android:pathPattern=".*\\.log" />
</intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name="sharedcode.turboeditor.activity.SelectFileActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/open_a_file"
android:exported="true"
android:parentActivityName=".HomeActivity"
android:theme="@style/AppThemeBaseLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".HomeActivity" />
<intent-filter>
<action android:name="android.intent.action.GET_CONTENT" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.OPENABLE" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<meta-data
android:name="com.sec.android.support.multiwindow"
android:value="true" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H"
android:value="598.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H"
android:value="598.0dip" />
<activity android:name="sharedcode.turboeditor.activity.MarkdownActivity" />
</application>
</manifest>

View File

@ -1,31 +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();
}
/*
* 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 com.maskyn.fileeditorpro;
import sharedcode.turboeditor.activity.MainActivity;
public class HomeActivity extends MainActivity {
@Override
public boolean showInterstitial() {
// nothing to do here
return false;
}
}

3
app/.gitignore vendored
View File

@ -1 +1,2 @@
/build
/build
manifest-merger-release-report.txt

View File

@ -1,68 +1,95 @@
/*
* 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/>.
*/
buildscript {
repositories {
maven { url 'http://download.crashlytics.com/maven' }
}
dependencies {
classpath 'com.crashlytics.tools.gradle:crashlytics-gradle:1.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'crashlytics'
repositories {
maven { url 'http://download.crashlytics.com/maven' }
}
android {
compileSdkVersion 20
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.maskyn.fileeditor"
minSdkVersion 14
targetSdkVersion 20
versionCode 26
versionName "1.10"
}
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:sharedCode')
compile 'com.google.android.gms:play-services:5.0.89'
compile 'com.crashlytics.android:crashlytics:1.+'
}
/*
* 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/>.
*/
import java.util.regex.Pattern
buildscript {
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/repo' }
}
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/repo' }
}
android {
compileSdkVersion 22
buildToolsVersion '22.0.1'
defaultConfig {
applicationId "com.maskyn.fileeditor"
minSdkVersion 11
targetSdkVersion 22
versionCode 47
versionName "1.19"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile project(':libraries:sharedCode')
//compile 'com.google.android.gms:play-services-ads:6.5.+'
compile('com.crashlytics.sdk.android:crashlytics:2.+@aar') {
transitive = true;
}
}
task incrementVersionCode << {
println(":incrementVersionCode - Incrementing Version Code...")
def buildGradleFile = file("build.gradle")
def patternVersionCode = Pattern.compile("versionCode (\\d+)")
def buildGradleFileText = buildGradleFile.getText()
def matcherVersionCode = patternVersionCode.matcher(buildGradleFileText)
matcherVersionCode.find()
def mVersionCode = Integer.parseInt(matcherVersionCode.group(1))
def mNextVersionCode = mVersionCode + 1
def manifestContent = matcherVersionCode.replaceAll("versionCode " + mNextVersionCode)
println(":incrementVersionCode - current versionCode=" + mVersionCode);
println(":incrementVersionCode - next versionCode=" + mNextVersionCode);
buildGradleFile.write(manifestContent)
}

View File

@ -1,17 +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 *;
#}
# 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 *;
#}

View File

@ -1,32 +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 com.maskyn.fileeditor;
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);
}
/*
* 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 com.maskyn.fileeditor;
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);
}
}

View File

@ -1,138 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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="com.maskyn.fileeditor"
android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/nome_app_turbo_editor"
android:hardwareAccelerated="false"
android:largeHeap="true"
android:supportsRtl="true"
>
<!-- android:alwaysRetainTaskState="true" -->
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
<activity
android:name=".HomeActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:launchMode="singleTop"
android:windowSoftInputMode="stateUnspecified|adjustPan"
android:theme="@style/AppTheme.Light.Editor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="text/*" />
<data android:pathPattern="*.txt" />
<data android:pathPattern="*.html" />
<data android:pathPattern="*.css" />
<data android:pathPattern="*.js" />
<data android:pathPattern="*.md"/>
<data android:pathPattern="*.php" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name="sharedcode.turboeditor.activity.PreferenceAbout"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/info"
android:parentActivityName=".HomeActivity"
android:theme="@style/AppTheme.Dark">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.maskyn.fileeditor.activity.HomeActivity" />
</activity>
<activity
android:name="sharedcode.turboeditor.activity.LicensesActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/open_source_license"
android:parentActivityName="sharedcode.turboeditor.activity.PreferenceAbout"
android:theme="@style/AppTheme.Light">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.maskyn.fileeditor.activity.PreferenceAbout" />
</activity>
<activity
android:name="sharedcode.turboeditor.activity.SelectFileActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/open_a_file"
android:parentActivityName=".HomeActivity"
android:theme="@style/AppTheme.Light">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.maskyn.fileeditor.activity.HomeActivity" />
</activity>
<meta-data
android:name="com.sec.android.support.multiwindow"
android:value="true" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H"
android:value="598.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H"
android:value="598.0dip" />
<activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
<meta-data android:name="com.crashlytics.ApiKey" android:value="672ab7531ce1e2e83c2ec6d84e8e94f2fa692c2a"/>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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="com.maskyn.fileeditor"
android:installLocation="auto">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/nome_app_turbo_editor"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:supportsRtl="true"
android:name="sharedcode.turboeditor.application.MyApp"
>
<!-- android:alwaysRetainTaskState="true" -->
<!--<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>-->
<activity
android:name=".HomeActivity"
android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
android:launchMode="singleTop"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:theme="@style/AppThemeEditorDark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.txt" />
<data android:pathPattern=".*\\.html" />
<data android:pathPattern=".*\\.css" />
<data android:pathPattern=".*\\.js" />
<data android:pathPattern=".*\\.md"/>
<data android:pathPattern=".*\\.php" />
<data android:pathPattern=".*\\.py" />
<data android:pathPattern=".*\\.org" />
</intent-filter>
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name="sharedcode.turboeditor.activity.SelectFileActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/open_a_file"
android:parentActivityName=".HomeActivity"
android:theme="@style/AppThemeBaseLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".HomeActivity" />
</activity>
<meta-data
android:name="com.sec.android.support.multiwindow"
android:value="true" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.DEFAULT_SIZE_H"
android:value="598.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_W"
android:value="632.0dip" />
<meta-data
android:name="com.sec.android.multiwindow.MINIMUM_SIZE_H"
android:value="598.0dip" />
<activity android:name="sharedcode.turboeditor.activity.MarkdownActivity" />
<!--<activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>-->
<meta-data android:name="com.crashlytics.ApiKey" android:value="672ab7531ce1e2e83c2ec6d84e8e94f2fa692c2a"/>
</application>
</manifest>

View File

@ -1,59 +1,45 @@
/*
* 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 com.maskyn.fileeditor;
import android.app.Activity;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
import java.util.Calendar;
import sharedcode.turboeditor.preferences.PreferenceHelper;
public class AdsHelper {
private Activity activity;
private InterstitialAd interstitial;
public AdsHelper(Activity activity) {
this.activity = activity;
int today = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
int lastDayAdShowed = PreferenceHelper.getLastDayAdShowed(activity);
boolean showAd = today != lastDayAdShowed;
if (showAd) {
interstitial = new InterstitialAd(activity);
interstitial.setAdUnitId("ca-app-pub-5679083452234719/7178038180");
// Create ad request.
AdRequest adRequest = new AdRequest.Builder().build();
// Begin loading your interstitial.
interstitial.loadAd(adRequest);
}
}
public void displayInterstitial() {
if (interstitial != null && interstitial.isLoaded()) {
interstitial.show();
int today = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
PreferenceHelper.setLastDayAdShowed(activity, today);
}
}
}
/*
* 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 com.maskyn.fileeditor;
import android.app.Activity;
/*import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;*/
public class AdsHelper {
//private InterstitialAd interstitial;
public AdsHelper(Activity activity) {
/*interstitial = new InterstitialAd(activity);
interstitial.setAdUnitId("ca-app-pub-5679083452234719/7178038180");
// Create ad request.
AdRequest adRequest = new AdRequest.Builder().build();
// Begin loading your interstitial.
interstitial.loadAd(adRequest);*/
}
public void displayInterstitial() {
//interstitial.show();
}
}

View File

@ -1,46 +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 com.maskyn.fileeditor;
import android.os.Bundle;
import com.crashlytics.android.Crashlytics;
import sharedcode.turboeditor.activity.BaseHomeActivity;
import sharedcode.turboeditor.preferences.PreferenceHelper;
public class HomeActivity extends BaseHomeActivity {
private AdsHelper adsHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(PreferenceHelper.getSendErrorReports(this))
Crashlytics.start(this);
// setup the ads
adsHelper = new AdsHelper(this);
}
@Override
public void displayInterstitial() {
adsHelper.displayInterstitial();
}
}
/*
* 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 com.maskyn.fileeditor;
import android.os.Bundle;
import com.crashlytics.android.Crashlytics;
import io.fabric.sdk.android.Fabric;
import sharedcode.turboeditor.activity.MainActivity;
public class HomeActivity extends MainActivity {
private AdsHelper adsHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(sharedcode.turboeditor.preferences.PreferenceHelper.getSendErrorReports(this))
Fabric.with(this, new Crashlytics());
// setup the ads
if(!sharedcode.turboeditor.util.ProCheckUtils.isPro(this))
adsHelper = new AdsHelper(this);
}
@Override
public boolean showInterstitial() {
if(adsHelper != null && !sharedcode.turboeditor.util.ProCheckUtils.isPro(this)) {
adsHelper.displayInterstitial();
return true;
}
else {
return false;
}
}
}

View File

@ -1,38 +1,38 @@
/*
* 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/>.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
/*
* 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/>.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<pre-dex-items>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\eventbus-2.2.1-32e81c5612ed132ff771b5425053d87f4f6c68c5.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\de.greenrobot\eventbus\2.2.1\a18ff12a9ab5ae52fd30d42f134517997568231e\eventbus-2.2.1.jar"
jumboMode="false"
revision="20.0.0"
sha1="a18ff12a9ab5ae52fd30d42f134517997568231e"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-24b647622164ce26bc5d0be361e05056efc68e13.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
jumboMode="false"
revision="20.0.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\debug\support-v4-19.0.1-f87b13e7ed00736c2050eda75093bf42b0503c64.jar"
jar="C:\Users\Vlad\AppData\Local\Android\android-sdk\extras\android\m2repository\com\android\support\support-v4\19.0.1\support-v4-19.0.1.jar"
jumboMode="false"
revision="20.0.0"
sha1="587da9a481ffd341d2fa091704489b89845ddacd"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\commons-io-2.4-63b64e68cd19031cd252ac65a3ef94421c1bf0f4.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\commons-io\commons-io\2.4\b1b6ea3b7e4aa4f492509a4952029cd8e48019ad\commons-io-2.4.jar"
jumboMode="false"
revision="20.0.0"
sha1="b1b6ea3b7e4aa4f492509a4952029cd8e48019ad"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\classes-da434e33e14290126d2ad09adf85ad0bbd76f025.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="2f3117da0016b1126fafe7fb332a45d2f910d76c"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\debug\classes-a52f37b7ca7cde7ab02ff10bba80d1c496284125.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="2f3117da0016b1126fafe7fb332a45d2f910d76c"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\support-v4-19.1.0-cf7033c44b86e758f85990185111614bac3ecfa8.jar"
jar="C:\Users\Vlad\AppData\Local\Android\android-sdk\extras\android\m2repository\com\android\support\support-v4\19.1.0\support-v4-19.1.0.jar"
jumboMode="false"
revision="20.0.0"
sha1="85f201b380937e61a9dce6ca90ccf6872abbfb67"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-2e38d54a78dd518320bb6abe3d8931a19ff26792.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
jumboMode="false"
revision="20.0.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\classes-2ad27a3265673aeeb8f40e4322d8e19509329c96.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\com.github.gabrielemariotti.changeloglib\library\1.5.1\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="74a89f0f8b56d9f11d70b8d8134cf4109f4797dc"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\debug\classes-bfc447e4dbe83598b94dbdc8e38492cbbda6ebb2.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="cdecd8167dfb75d5785decb911fc4516445dd6a6"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\release\classes-579a5ce52888f504a812e0f68758a11f20a21c15.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="a593d4ce7ccdfa1eac8d97a82db64f23614b59a1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\debug\classes-0a2ec632e1127b260f2b888ca5539fd41a1a638b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\exploded-aar\com.github.gabrielemariotti.changeloglib\library\1.5.1\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="74a89f0f8b56d9f11d70b8d8134cf4109f4797dc"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\classes-a8678f35ea62f18f2f7b60a15203aa6c74b5e559.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\com.google.android.gms\play-services\5.0.89\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="d71573c9c5ea98a8db47ad6ff993a63d492b3bfa"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\pre-dexed\release\classes-3e47e18f46719c7bf296f4b49ff03aaa3d406ba6.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app-pro\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="a593d4ce7ccdfa1eac8d97a82db64f23614b59a1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\release\crashlytics-1.1.13-7bbec76a44aa9cc3279ec6bff22b2c929a8abf67.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\com.crashlytics.android\crashlytics\1.1.13\e821eafa1bf489a26bdb71f95078c26785b37a1\crashlytics-1.1.13.jar"
jumboMode="false"
revision="20.0.0"
sha1="0e821eafa1bf489a26bdb71f95078c26785b37a1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\pre-dexed\debug\classes-c9a98cf79b48106440348a9fa210060d1b8b2647.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\TurboMaterialEditor\app\build\intermediates\exploded-aar\TurboMaterialEditor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="cdecd8167dfb75d5785decb911fc4516445dd6a6"/>
</pre-dex-items>

Binary file not shown.

View File

@ -1,37 +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/>.
#
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Settings specified in this file will override any Gradle settings
# configured through the IDE.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
#
# 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/>.
#
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Settings specified in this file will override any Gradle settings
# configured through the IDE.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

View File

@ -1,25 +1,6 @@
#
# 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/>.
#
#Wed Apr 10 15:27:10 PDT 2013
#Thu Dec 11 18:32:51 CET 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-all.zip

328
gradlew vendored
View File

@ -1,164 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

180
gradlew.bat vendored
View File

@ -1,90 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1 @@
/build

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.faizmalkani.floatingactionbutton">

View File

@ -1,12 +1,11 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
compileSdkVersion 22
buildToolsVersion '22.0.1'
defaultConfig {
applicationId 'com.faizmalkani.floatingactionbutton'
minSdkVersion 14
targetSdkVersion 19
minSdkVersion 7
targetSdkVersion 22
versionName "1.0"
versionCode 1
}
@ -22,7 +21,12 @@ android {
assets.srcDirs = ['assets']
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
compile 'com.nineoldandroids:library:2.4.0'
}

View File

@ -1,13 +0,0 @@
/**
* 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";
}

View File

@ -1,13 +0,0 @@
/**
* 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";
}

View File

@ -1,13 +0,0 @@
/**
* 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";
}

View File

@ -1,173 +0,0 @@
/* 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;
};
}

View File

@ -1,173 +0,0 @@
/* 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;
};
}

View File

@ -1,27 +0,0 @@
/* 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;
}
}

View File

@ -1,173 +0,0 @@
/* 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;
};
}

View File

@ -1,14 +0,0 @@
<?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>

View File

@ -1,13 +0,0 @@
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

View File

@ -1,16 +0,0 @@
<?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>

View File

@ -1,14 +0,0 @@
<?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>

View File

@ -1,13 +0,0 @@
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

View File

@ -1,16 +0,0 @@
<?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>

View File

@ -1,11 +0,0 @@
<?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>

View File

@ -1,11 +0,0 @@
<?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>

View File

@ -1,11 +0,0 @@
<?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>

View File

@ -1,38 +0,0 @@
<?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>

View File

@ -1,38 +0,0 @@
<?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>

View File

@ -1,38 +0,0 @@
<?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>

View File

@ -1,20 +0,0 @@
<?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>

View File

@ -1,16 +0,0 @@
<?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>

View File

@ -1,13 +0,0 @@
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

View File

@ -1,2 +0,0 @@
Manifest-Version: 1.0

View File

@ -1,2 +0,0 @@
Manifest-Version: 1.0

View File

@ -1,4 +1,3 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FloatingActionButton">
<attr name="drawable" format="integer"/>
<attr name="color" format="color"/>
<attr name="colour" format="color"/>
<attr name="shadowRadius" format="float"/>
<attr name="shadowDx" format="float"/>
<attr name="shadowDy" format="float"/>

View File

@ -1,10 +1,27 @@
/*
* Copyright (c) 2014 SBG Apps
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.faizmalkani.floatingactionbutton;
import android.view.View;
import android.widget.AbsListView;
/**
* Created by Stéphane on 09/07/2014.
*/
class DirectionScrollListener implements AbsListView.OnScrollListener {
private static final int DIRECTION_CHANGE_THRESHOLD = 1;
@ -34,10 +51,7 @@ class DirectionScrollListener implements AbsListView.OnScrollListener {
goingDown = firstVisibleItem > mPrevPosition;
}
if (changed && mUpdated) {
if(goingDown)
mFloatingActionButton.hideFab();
else
mFloatingActionButton.showFab();
mFloatingActionButton.hide(goingDown);
}
mPrevPosition = firstVisibleItem;
mPrevTop = firstViewTop;

View File

@ -1,9 +1,7 @@
package com.faizmalkani.floatingactionbutton;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@ -11,135 +9,146 @@ import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
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.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
public class FloatingActionButton extends View
{
Context _context;
Paint mButtonPaint, mDrawablePaint;
Bitmap mBitmap;
int mScreenHeight;
float currentY;
boolean mHidden = false;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.view.ViewHelper;
ObjectAnimator mShowAnimation;
ObjectAnimator mHideAnimation;
public class FloatingActionButton extends View {
public FloatingActionButton(Context context, AttributeSet attributeSet)
{
super(context, attributeSet);
_context = context;
init(Color.WHITE);
private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
private final Paint mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mDrawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBitmap;
private int mColor;
private boolean mHidden = false;
/**
* The FAB button's Y position when it is displayed.
*/
private float mYDisplayed = -1;
/**
* The FAB button's Y position when it is hidden.
*/
private float mYHidden = -1;
public FloatingActionButton(Context context) {
this(context, null);
}
@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 FloatingActionButton(Context context, AttributeSet attributeSet) {
this(context, attributeSet, 0);
}
public void init(int fabColor)
{
setWillNotDraw(false);
this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mButtonPaint.setColor(fabColor);
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FloatingActionButton);
mColor = a.getColor(R.styleable.FloatingActionButton_colour, Color.WHITE);
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();
mButtonPaint.setColor(mColor);
float radius, dx, dy;
radius = a.getFloat(R.styleable.FloatingActionButton_shadowRadius, 10.0f);
dx = a.getFloat(R.styleable.FloatingActionButton_shadowDx, 0.0f);
dy = a.getFloat(R.styleable.FloatingActionButton_shadowDy, 3.5f);
int color = a.getInteger(R.styleable.FloatingActionButton_shadowColor, Color.argb(100, 0, 0, 0));
mButtonPaint.setShadowLayer(radius, dx, dy, color);
WindowManager mWindowManager = (WindowManager) _context.getSystemService(Context.WINDOW_SERVICE);
Drawable drawable = a.getDrawable(R.styleable.FloatingActionButton_drawable);
if (null != drawable) {
mBitmap = ((BitmapDrawable) drawable).getBitmap();
}
setWillNotDraw(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
WindowManager mWindowManager = (WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
Display display = mWindowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
mScreenHeight = size.y;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
mYHidden = size.y;
} else mYHidden = display.getHeight();
}
mShowAnimation = ObjectAnimator.ofFloat(this, "Y", currentY);
mHideAnimation = ObjectAnimator.ofFloat(this, "Y", mScreenHeight);
public static int darkenColor(int color) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= 0.8f;
return Color.HSVToColor(hsv);
}
public void setColor(int color) {
mColor = color;
mButtonPaint.setColor(mColor);
invalidate();
}
public void setDrawable(Drawable drawable) {
mBitmap = ((BitmapDrawable) drawable).getBitmap();
invalidate();
}
@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);
protected void onDraw(Canvas canvas) {
canvas.drawCircle(getWidth() / 2, getHeight() / 2, (float) (getWidth() / 2.6), mButtonPaint);
if (null != mBitmap) {
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);
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// Perform the default behavior
super.onLayout(changed, left, top, right, bottom);
// Store the FAB button's displayed Y position if we are not already aware of it
if (mYDisplayed == -1) {
mYDisplayed = ViewHelper.getY(this);
}
else if(event.getAction() == MotionEvent.ACTION_DOWN)
{
setAlpha(0.6f);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int color;
if (event.getAction() == MotionEvent.ACTION_UP) {
color = mColor;
} else {
color = darkenColor(mColor);
}
mButtonPaint.setColor(color);
invalidate();
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 hide(boolean hide) {
// If the hidden state is being updated
if (mHidden != hide) {
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;
// Store the new hidden state
mHidden = hide;
// Animate the FAB to it's new Y position
ObjectAnimator animator = ObjectAnimator.ofFloat(this, "y", mHidden ? mYHidden : mYDisplayed).setDuration(500);
animator.setInterpolator(mInterpolator);
animator.start();
}
}
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));
}
}
}

View File

@ -1,33 +0,0 @@
#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

View File

@ -1,26 +0,0 @@
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
}
}

View File

@ -1,15 +0,0 @@
<?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>

View File

@ -1,43 +0,0 @@
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);
}
}

View File

@ -1,176 +0,0 @@
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;
}
}

View File

@ -1,36 +0,0 @@
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;
}
}

View File

@ -1,331 +0,0 @@
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();
}
}

View File

@ -1,108 +0,0 @@
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) {
// // }
// // }
// // }
// }
}

View File

@ -1,809 +0,0 @@
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();
}
}
}

View File

@ -1,155 +0,0 @@
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);
}
}
}

View File

@ -1,51 +0,0 @@
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);
}

View File

@ -1,29 +0,0 @@
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;
}
}

View File

@ -1,31 +0,0 @@
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;
}
}

View File

@ -1,18 +0,0 @@
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);
}
}

View File

@ -1,69 +0,0 @@
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);
}
}

View File

@ -1,18 +0,0 @@
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);
}
}

View File

@ -1,16 +0,0 @@
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);
}
}

View File

@ -1,89 +0,0 @@
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;
}
}

View File

@ -1 +1 @@
/build
/build

View File

@ -1,61 +1,74 @@
/*
* 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'
}
/*
* 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 22
buildToolsVersion '22.0.1'
defaultConfig {
minSdkVersion 11
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':libraries:FloatingActionButton')
// compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
compile 'org.apache.commons:commons-lang3:+'
compile files('libs/juniversalchardet-1.0.3.jar')
compile('com.android.support:appcompat-v7:22.+') {
exclude group: 'com.android.support', module: 'support-v4'
}
compile 'com.android.support:support-v4:22.+'
compile 'com.github.gabrielemariotti.changeloglib:library:+'
compile 'commons-io:commons-io:2.4'
compile 'com.android.support:support-annotations:+'
compile files('libs/markdownview-1.2.jar')
}

Binary file not shown.

View File

@ -1,17 +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 *;
#}
# 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 *;
#}

View File

@ -1,32 +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);
}
/*
* 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);
}
}

View File

@ -1,24 +1,23 @@
<!--
~ 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>
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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>

View File

@ -0,0 +1,144 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.vending.billing;
import android.os.Bundle;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog
* RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
interface IInAppBillingService {
/**
* Checks support for the requested billing API version, package and in-app type.
* Minimum API version supported by this interface is 3.
* @param apiVersion the billing version which the app is using
* @param packageName the package name of the calling app
* @param type type of the in-app item being purchased "inapp" for one-time purchases
* and "subs" for subscription.
* @return RESULT_OK(0) on success, corresponding result code on failures
*/
int isBillingSupported(int apiVersion, String packageName, String type);
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the Third-party is using
* @param packageName the package name of the calling app
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00",
* "title : "Example Title", "description" : "This is an example description" }'
*/
Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle);
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type the type of the in-app item ("inapp" for one-time purchases
* and "subs" for subscription).
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
* TODO: change this to app-specific keys.
*/
Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type,
String developerPayload);
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type the type of the in-app items being requested
* ("inapp" for one-time purchases and "subs" for subscription).
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on
* failure as listed above.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken);
/**
* Consume the last purchase of the given SKU. This will result in this item being removed
* from all subsequent responses to getPurchases() and allow re-purchase of this item.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param purchaseToken token in the purchase information JSON that identifies the purchase
* to be consumed
* @return 0 if consumption succeeded. Appropriate error values for failures.
*/
int consumePurchase(int apiVersion, String packageName, String purchaseToken);
}

View File

@ -0,0 +1,112 @@
@charset "utf-8";
/**
* markdown.css
*
* This program 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 3 of the License, or (at your option) any
* later version.
*
* This program 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 program. If not, see http://gnu.org/licenses/lgpl.txt.
*
* @project Weblog and Open Source Projects of Florian Wolters
* @version GIT: $Id$
* @package xhtml-css
* @author Florian Wolters <florian.wolters.85@googlemail.com>
* @copyright 2012 Florian Wolters
* @cssdoc version 1.0-pre
* @license http://gnu.org/licenses/lgpl.txt GNU Lesser General Public License
* @link http://github.com/FlorianWolters/jekyll-bootstrap-theme
* @media all
* @valid true
*/
body {
font-family: Helvetica, Arial, Freesans, clean, sans-serif;
padding:1em;
margin:auto;
max-width:42em;
background:#fefefe;
}
h1, h2, h3, h4, h5, h6 {
font-weight: bold;
}
h1 {
color: #000000;
font-size: 28px;
}
h2 {
border-bottom: 1px solid #CCCCCC;
color: #000000;
font-size: 24px;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777777;
background-color: inherit;
font-size: 14px;
}
hr {
height: 0.2em;
border: 0;
color: #CCCCCC;
background-color: #CCCCCC;
}
p, blockquote, ul, ol, dl, li, table, pre {
margin: 15px 0;
}
code, pre {
border-radius: 3px;
background-color: #F8F8F8;
color: inherit;
}
code {
border: 1px solid #EAEAEA;
margin: 0 2px;
padding: 0 5px;
}
pre {
border: 1px solid #CCCCCC;
line-height: 1.25em;
overflow: auto;
padding: 6px 10px;
}
pre > code {
border: 0;
margin: 0;
padding: 0;
}
a, a:visited {
color: #4183C4;
background-color: inherit;
text-decoration: none;
}

View File

@ -0,0 +1,119 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.os.Build;
import android.os.Process;
public class Common {
private static Boolean oEmulator = false;
public static final String TAG = Common.class.getPackage().getName();
public static Boolean DEBUG = true;
public static String[] BINARIES = new String[]{null, "busybox", "toolbox"};
public static Map<String, Integer> UIDS = new HashMap<String, Integer>();
public static Map<Integer, String> UNAMES = new HashMap<Integer, String>();
static {
UIDS.put("root", 0);
UIDS.put("system", 1000);
UIDS.put("radio", 1001);
UIDS.put("bluetooth", 1002);
UIDS.put("graphics", 1003);
UIDS.put("input", 1004);
UIDS.put("audio", 1005);
UIDS.put("camera", 1006);
UIDS.put("log", 1007);
UIDS.put("compass", 1008);
UIDS.put("mount", 1009);
UIDS.put("wifi", 1010);
UIDS.put("adb", 1011);
UIDS.put("install", 1012);
UIDS.put("media", 1013);
UIDS.put("dhcp", 1014);
UIDS.put("shell", 2000);
UIDS.put("cache", 2001);
UIDS.put("diag", 2002);
UIDS.put("net_bt_admin", 3001);
UIDS.put("net_bt", 3002);
UIDS.put("inet", 3003);
UIDS.put("net_raw", 3004);
UIDS.put("misc", 9998);
UIDS.put("nobody", 9999);
for (Entry<String, Integer> entry : UIDS.entrySet()) {
UNAMES.put(entry.getValue(), entry.getKey());
}
oEmulator = Build.BRAND.equalsIgnoreCase("generic") ||
Build.MODEL.contains("google_sdk") ||
Build.MODEL.contains("Emulator") ||
Build.MODEL.contains("Android SDK");
}
/**
* Check if the current device is an emulator
*/
public static Boolean isEmulator() {
return oEmulator;
}
public static Integer getUID(String name) {
if (name != null) {
if (name.matches("^[0-9]+$")) {
return Integer.parseInt(name);
}
if (UIDS.containsKey(name)) {
return UIDS.get(name);
} else if (name.startsWith("u")) {
Integer sep = name.indexOf("_");
if (sep > 0) {
Integer uid = Integer.parseInt( name.substring(1, sep) );
Integer gid = Integer.parseInt( name.substring(sep+2) );
return uid * 100000 + ((Process.FIRST_APPLICATION_UID + gid) % 100000);
}
}
}
return -10000;
}
public static String getUIDName(Integer id) {
if (UNAMES.containsKey(id)) {
return UNAMES.get(id);
} else if (id >= 10000) {
Integer uid = id / 100000;
Integer gid = id % Process.FIRST_APPLICATION_UID;
return "u" + uid + "_a" + gid + "";
}
return null;
}
}

View File

@ -0,0 +1,348 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4;
import java.util.HashSet;
import java.util.Set;
import com.spazedog.lib.rootfw4.Shell.Attempts;
import com.spazedog.lib.rootfw4.Shell.OnShellConnectionListener;
import com.spazedog.lib.rootfw4.Shell.OnShellResultListener;
import com.spazedog.lib.rootfw4.Shell.OnShellValidateListener;
import com.spazedog.lib.rootfw4.Shell.Result;
import com.spazedog.lib.rootfw4.utils.Device;
import com.spazedog.lib.rootfw4.utils.Device.Process;
import com.spazedog.lib.rootfw4.utils.File;
import com.spazedog.lib.rootfw4.utils.Filesystem;
import com.spazedog.lib.rootfw4.utils.Memory;
import com.spazedog.lib.rootfw4.utils.Filesystem.Disk;
import com.spazedog.lib.rootfw4.utils.Memory.CompCache;
import com.spazedog.lib.rootfw4.utils.Memory.Swap;
import com.spazedog.lib.rootfw4.utils.io.FileReader;
import com.spazedog.lib.rootfw4.utils.io.FileWriter;
/**
* This is a global static front-end to {@link Shell}. It allows one global shell connection to be
* easily shared across classes and threads without having to create multiple connections.
*/
public class RootFW {
protected static volatile Shell mShell;
protected static volatile Integer mLockCount = 0;
protected static final Object mLock = new Object();
protected static Set<OnConnectionListener> mListeners = new HashSet<OnConnectionListener>();
/**
* An interface that can be used to monitor the current state of the global connection.
*
* @see #addConnectionListener(OnConnectionListener)
*/
public static interface OnConnectionListener extends OnShellConnectionListener {
/**
* Invoked when the shell has been connected
*/
public void onShellConnect();
}
/**
* Create a new connection to the global shell.<br /><br />
*
* Note that this method will fallback on a normal user shell if root could not be obtained.
* Therefore it is always a good idea to check the state of root using {@link #isRoot()}
*
* @return
* True if the shell was connected successfully
*/
public static Boolean connect() {
synchronized(mLock) {
if (mShell == null || !mShell.isConnected()) {
mLockCount = 0;
mShell = new Shell(true);
/*
* Fallback to a regular user shell
*/
if (!mShell.isConnected()) {
mShell = new Shell(false);
}
mShell.addShellConnectionListener(new OnShellConnectionListener(){
@Override
public void onShellDisconnect() {
for (OnConnectionListener listener : mListeners) {
listener.onShellDisconnect();
}
}
});
for (OnConnectionListener listener : mListeners) {
listener.onShellConnect();
}
}
return mShell.isConnected();
}
}
/**
* @see #disconnect(Boolean)
*/
public static void disconnect() {
disconnect(false);
}
/**
* Destroy the connection to the global shell.<br /><br />
*
* The connection will only be destroyed if there is no current locks on this connecton.
*
* @see #lock()
*/
public static void disconnect(Boolean force) {
synchronized(mLock) {
if (mLockCount == 0 || force) {
mLockCount = 0;
mShell.destroy();
mShell = null;
}
}
}
/**
* Add a new lock on this connection. Each call to this method will add an additional lock.
* As long as there are 1 or more locks on this connection, it cannot be destroyed using {@link #disconnect()}
*
* @see #unlock()
*/
public static void lock() {
synchronized(mLock) {
mLockCount += 1;
}
}
/**
* Removes one lock from this connection. Each call will remove 1 lock as long as there are 1 or more locks attached.
*
* @see #lock()
*/
public static void unlock() {
synchronized(mLock) {
if (mLockCount > 0) {
mLockCount -= 1;
} else {
mLockCount = 0;
}
}
}
/**
* Checks if there are any active locks on the connection.
*/
public static Boolean isLocked() {
synchronized(mLock) {
return mLockCount == 0;
}
}
/**
* Add a new {@link OnConnectionListener} to the global shell
*
* @see #removeConnectionListener(OnConnectionListener)
*/
public static void addConnectionListener(OnConnectionListener listener) {
synchronized(mLock) {
mListeners.add(listener);
}
}
/**
* Remove a {@link OnConnectionListener} from the global shell
*
* @see #addConnectionListener(OnConnectionListener)
*/
public static void removeConnectionListener(OnConnectionListener listener) {
synchronized(mLock) {
mListeners.remove(listener);
}
}
/**
* @see Shell#execute(String)
*/
public static Result execute(String command) {
return mShell.execute(command);
}
/**
* @see Shell#execute(String[])
*/
public static Result execute(String[] commands) {
return mShell.execute(commands);
}
/**
* @see Shell#execute(String[], Integer[], OnShellValidateListener)
*/
public static Result execute(String[] commands, Integer[] resultCodes, OnShellValidateListener validater) {
return mShell.execute(commands, resultCodes, validater);
}
/**
* @see Shell#executeAsync(String, OnShellResultListener)
*/
public static void executeAsync(String command, OnShellResultListener listener) {
mShell.executeAsync(command, listener);
}
/**
* @see Shell#executeAsync(String[], OnShellResultListener)
*/
public static void executeAsync(String[] commands, OnShellResultListener listener) {
mShell.executeAsync(commands, listener);
}
/**
* @see Shell#executeAsync(String[], Integer[], OnShellValidateListener, OnShellResultListener)
*/
public static void executeAsync(String[] commands, Integer[] resultCodes, OnShellValidateListener validater, OnShellResultListener listener) {
mShell.executeAsync(commands, resultCodes, validater, listener);
}
/**
* @see Shell#isRoot()
*/
public static Boolean isRoot() {
return mShell.isRoot();
}
/**
* @see Shell#isConnected()
*/
public static Boolean isConnected() {
return mShell != null && mShell.isConnected();
}
/**
* @see Shell#getTimeout()
*/
public static Integer getTimeout() {
return mShell.getTimeout();
}
/**
* @see Shell#setTimeout(Integer)
*/
public static void setTimeout(Integer timeout) {
mShell.setTimeout(timeout);
}
/**
* @see Shell#getBinary(String)
*/
public static String findCommand(String bin) {
return mShell.findCommand(bin);
}
/**
* @see Shell#createAttempts(String)
*/
public static Attempts createAttempts(String command) {
return mShell.createAttempts(command);
}
/**
* @see Shell#getFileReader(String)
*/
public static FileReader getFileReader(String file) {
return mShell.getFileReader(file);
}
/**
* @see Shell#getFileWriter(String, Boolean)
*/
public static FileWriter getFileWriter(String file, Boolean append) {
return mShell.getFileWriter(file, append);
}
/**
* @see Shell#getFile(String)
*/
public static File getFile(String file) {
return mShell.getFile(file);
}
/**
* @see Shell#getFilesystem()
*/
public static Filesystem getFilesystem() {
return mShell.getFilesystem();
}
/**
* @see Shell#getDisk(String)
*/
public static Disk getDisk(String disk) {
return mShell.getDisk(disk);
}
/**
* @see Shell#getDevice()
*/
public static Device getDevice() {
return mShell.getDevice();
}
/**
* @see Shell#getProcess(String)
*/
public static Process getProcess(String process) {
return mShell.getProcess(process);
}
/**
* @see Shell#getProcess(Integer)
*/
public static Process getProcess(Integer pid) {
return mShell.getProcess(pid);
}
/**
* @see Shell#getMemory()
*/
public static Memory getMemory() {
return mShell.getMemory();
}
/**
* @see Shell#getCompCache()
*/
public static CompCache getCompCache() {
return mShell.getCompCache();
}
/**
* @see Shell#getSwap(String device)
*/
public static Swap getSwap(String device) {
return mShell.getSwap(device);
}
}

View File

@ -0,0 +1,789 @@
/*
* 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 com.spazedog.lib.rootfw4;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import android.os.Bundle;
import android.util.Log;
import com.spazedog.lib.rootfw4.ShellStream.OnStreamListener;
import com.spazedog.lib.rootfw4.containers.Data;
import com.spazedog.lib.rootfw4.utils.Device;
import com.spazedog.lib.rootfw4.utils.Device.Process;
import com.spazedog.lib.rootfw4.utils.File;
import com.spazedog.lib.rootfw4.utils.Filesystem;
import com.spazedog.lib.rootfw4.utils.Filesystem.Disk;
import com.spazedog.lib.rootfw4.utils.Memory;
import com.spazedog.lib.rootfw4.utils.Memory.CompCache;
import com.spazedog.lib.rootfw4.utils.Memory.Swap;
import com.spazedog.lib.rootfw4.utils.io.FileReader;
import com.spazedog.lib.rootfw4.utils.io.FileWriter;
/**
* This class is a front-end to {@link ShellStream} which makes it easier to work
* with normal shell executions. If you need to execute a consistent command (one that never ends),
* you should work with {@link ShellStream} directly.
*/
public class Shell {
public static final String TAG = Common.TAG + ".Shell";
protected static Set<Shell> mInstances = Collections.newSetFromMap(new WeakHashMap<Shell, Boolean>());
protected static Map<String, String> mBinaries = new HashMap<String, String>();
protected Set<OnShellBroadcastListener> mBroadcastRecievers = Collections.newSetFromMap(new WeakHashMap<OnShellBroadcastListener, Boolean>());
protected Set<OnShellConnectionListener> mConnectionRecievers = new HashSet<OnShellConnectionListener>();
protected Object mLock = new Object();
protected ShellStream mStream;
protected Boolean mIsConnected = false;
protected Boolean mIsRoot = false;
protected List<String> mOutput = null;
protected Integer mResultCode = 0;
protected Integer mShellTimeout = 15000;
protected Set<Integer> mResultCodes = new HashSet<Integer>();
/**
* This interface is used internally across utility classes.
*/
public static interface OnShellBroadcastListener {
public void onShellBroadcast(String key, Bundle data);
}
/**
* This interface is for use with {@link Shell#executeAsync(String[], OnShellResultListener)}.
*/
public static interface OnShellResultListener {
/**
* Called when an asynchronous execution has finished.
*
* @param result
* The result from the asynchronous execution
*/
public void onShellResult(Result result);
}
/**
* This interface is for use with the execute methods. It can be used to validate an attempt command, if that command
* cannot be validated by result code alone.
*/
public static interface OnShellValidateListener {
/**
* Called at the end of each attempt in order to validate it. If this method returns false, then the next attempt will be executed.
*
* @param command
* The command that was executed during this attempt
*
* @param result
* The result code from this attempt
*
* @param output
* The output from this attempt
*
* @param resultCodes
* All of the result codes that has been added as successful ones
*
* @return
* False to continue to the next attempts, or True to stop the current execution
*/
public Boolean onShellValidate(String command, Integer result, List<String> output, Set<Integer> resultCodes);
}
/**
* This interface is used to get information about connection changes.
*/
public static interface OnShellConnectionListener {
/**
* Called when the connection to the shell is lost.
*/
public void onShellDisconnect();
}
/**
* This class is used to store the result from shell executions.
* It extends the {@link Data} class.
*/
public static class Result extends Data<Result> {
private Integer mResultCode;
private Integer[] mValidResults;
private Integer mCommandNumber;
public Result(String[] lines, Integer result, Integer[] validResults, Integer commandNumber) {
super(lines);
mResultCode = result;
mValidResults = validResults;
mCommandNumber = commandNumber;
}
/**
* Get the result code from the shell execution.
*/
public Integer getResultCode() {
return mResultCode;
}
/**
* Compare the result code with {@link Shell#addResultCode(Integer)} to determine
* whether or not the execution was a success.
*/
public Boolean wasSuccessful() {
for (int i=0; i < mValidResults.length; i++) {
if ((int) mValidResults[i] == (int) mResultCode) {
return true;
}
}
return false;
}
/**
* Get the command number that produced a successful result.
*/
public Integer getCommandNumber() {
return mCommandNumber;
}
}
/**
* A class containing automatically created shell attempts and links to both {@link Shell#executeAsync(String[], Integer[], OnShellResultListener)} and {@link Shell#execute(String[], Integer[])} <br /><br />
*
* All attempts are created based on {@link Common#BINARIES}. <br /><br />
*
* Example: String("ls") would become String["ls", "busybox ls", "toolbox ls"] if {@link Common#BINARIES} equals String[null, "busybox", "toolbox"].<br /><br />
*
* You can also apply the keyword %binary if you need to apply the binaries to more than the beginning of a command. <br /><br />
*
* Example: String("(%binary test -d '%binary pwd') || exit 1") would become String["(test -d 'pwd') || exit 1", "(busybox test -d 'busybox pwd') || exit 1", "(toolbox test -d 'toolbox pwd') || exit 1"]
*
* @see Shell#createAttempts(String)
*/
public class Attempts {
protected String[] mAttempts;
protected Integer[] mResultCodes;
protected OnShellValidateListener mValidateListener;
protected OnShellResultListener mResultListener;
protected Attempts(String command) {
if (command != null) {
Integer pos = 0;
mAttempts = new String[ Common.BINARIES.length ];
for (String binary : Common.BINARIES) {
if (command.contains("%binary ")) {
mAttempts[pos] = command.replaceAll("%binary ", (binary != null && binary.length() > 0 ? binary + " " : ""));
} else {
mAttempts[pos] = (binary != null && binary.length() > 0 ? binary + " " : "") + command;
}
pos += 1;
}
}
}
public Attempts setValidateListener(OnShellValidateListener listener) {
mValidateListener = listener; return this;
}
public Attempts setResultListener(OnShellResultListener listener) {
mResultListener = listener; return this;
}
public Attempts setResultCodes(Integer... resultCodes) {
mResultCodes = resultCodes; return this;
}
public Result execute(OnShellValidateListener listener) {
return setValidateListener(listener).execute();
}
public Result execute() {
return Shell.this.execute(mAttempts, mResultCodes, mValidateListener);
}
public void executeAsync(OnShellResultListener listener) {
setResultListener(listener).executeAsync();
}
public void executeAsync() {
Shell.this.executeAsync(mAttempts, mResultCodes, mValidateListener, mResultListener);
}
}
/**
* Establish a {@link ShellStream} connection.
*
* @param requestRoot
* Whether or not to request root privileges for the shell connection
*/
public Shell(Boolean requestRoot) {
mResultCodes.add(0);
mIsRoot = requestRoot;
/*
* Kutch superuser daemon mode sometimes has problems connecting the first time.
* So we will give it two tries before giving up.
*/
for (int i=0; i < 2; i++) {
if(Common.DEBUG)Log.d(TAG, "Construct: Running connection attempt number " + (i+1));
mStream = new ShellStream(requestRoot, new OnStreamListener() {
@Override
public void onStreamStart() {
if(Common.DEBUG)Log.d(TAG, "onStreamStart: ...");
mOutput = new ArrayList<String>();
}
@Override
public void onStreamInput(String outputLine) {
if(Common.DEBUG)Log.d(TAG, "onStreamInput: " + (outputLine != null ? (outputLine.length() > 50 ? outputLine.substring(0, 50) + " ..." : outputLine) : "NULL"));
mOutput.add(outputLine);
}
@Override
public void onStreamStop(Integer resultCode) {
if(Common.DEBUG)Log.d(TAG, "onStreamStop: " + resultCode);
mResultCode = resultCode;
}
@Override
public void onStreamDied() {
if(Common.DEBUG)Log.d(TAG, "onStreamDied: The stream has been closed");
if (mIsConnected) {
if(Common.DEBUG)Log.d(TAG, "onStreamDied: The stream seams to have died, reconnecting");
mStream = new ShellStream(mIsRoot, this);
if (mStream.isActive()) {
Result result = execute("echo connected");
mIsConnected = result != null && "connected".equals(result.getLine());
} else {
if(Common.DEBUG)Log.d(TAG, "onStreamDied: Could not reconnect");
mIsConnected = false;
}
}
if (!mIsConnected) {
for (OnShellConnectionListener reciever : mConnectionRecievers) {
reciever.onShellDisconnect();
}
}
}
});
if (mStream.isActive()) {
Result result = execute("echo connected");
mIsConnected = result != null && "connected".equals(result.getLine());
if (mIsConnected) {
if(Common.DEBUG)Log.d(TAG, "Construct: Connection has been established");
mInstances.add(this); break;
}
}
}
}
/**
* Execute a shell command.
*
* @see Shell#execute(String[], Integer[])
*
* @param command
* The command to execute
*/
public Result execute(String command) {
return execute(new String[]{command}, null, null);
}
/**
* Execute a range of commands until one is successful.
*
* @see Shell#execute(String[], Integer[])
*
* @param commands
* The commands to try
*/
public Result execute(String[] commands) {
return execute(commands, null, null);
}
/**
* Execute a range of commands until one is successful.<br /><br />
*
* Android shells differs a lot from one another, which makes it difficult to program shell scripts for.
* This method can help with that by trying different commands until one works. <br /><br />
*
* <code>Shell.execute( new String(){"cat file", "toolbox cat file", "busybox cat file"} );</code><br /><br />
*
* Whether or not a command was successful, depends on {@link Shell#addResultCode(Integer)} which by default only contains '0'.
* The command number that was successful can be checked using {@link Result#getCommandNumber()}.
*
* @param commands
* The commands to try
*
* @param resultCodes
* Result Codes representing successful execution. These will be temp. merged with {@link Shell#addResultCode(Integer)}.
*
* @param validater
* A {@link OnShellValidateListener} instance or NULL
*/
public Result execute(String[] commands, Integer[] resultCodes, OnShellValidateListener validater) {
synchronized(mLock) {
if (mStream.waitFor(mShellTimeout)) {
Integer cmdCount = 0;
Set<Integer> codes = new HashSet<Integer>(mResultCodes);
if (resultCodes != null) {
Collections.addAll(codes, resultCodes);
}
for (String command : commands) {
if(Common.DEBUG)Log.d(TAG, "execute: Executing the command '" + command + "'");
mStream.execute(command);
if(!mStream.waitFor(mShellTimeout)) {
/*
* Something is wrong, reconnect to the shell.
*/
mStream.destroy();
return null;
}
if(Common.DEBUG)Log.d(TAG, "execute: The command finished with the result code '" + mResultCode + "'");
if ((validater != null && validater.onShellValidate(command, mResultCode, mOutput, codes)) || codes.contains(mResultCode)) {
/*
* If a validater excepts this, then add the result code to the list of successful codes
*/
codes.add(mResultCode); break;
}
cmdCount += 1;
}
if (mOutput != null) {
return new Result(mOutput.toArray(new String[mOutput.size()]), mResultCode, codes.toArray(new Integer[codes.size()]), cmdCount);
}
}
return null;
}
}
/**
* Execute a shell command asynchronous.
*
* @see Shell#executeAsync(String[], Integer[], OnShellResultListener)
*
* @param command
* The command to execute
*
* @param listener
* A {@link OnShellResultListener} callback instance
*/
public void executeAsync(String command, OnShellResultListener listener) {
executeAsync(new String[]{command}, null, null, listener);
}
/**
* Execute a range of commands asynchronous until one is successful.
*
* @see Shell#executeAsync(String[], Integer[], OnShellResultListener)
*
* @param commands
* The commands to try
*
* @param listener
* A {@link OnShellResultListener} callback instance
*/
public void executeAsync(String[] commands, OnShellResultListener listener) {
executeAsync(commands, null, null, listener);
}
/**
* Execute a range of commands asynchronous until one is successful.
*
* @see Shell#execute(String[], Integer[])
*
* @param commands
* The commands to try
*
* @param resultCodes
* Result Codes representing successful execution. These will be temp. merged with {@link Shell#addResultCode(Integer)}.
*
* @param validater
* A {@link OnShellValidateListener} instance or NULL
*
* @param listener
* A {@link OnShellResultListener} callback instance
*/
public synchronized void executeAsync(final String[] commands, final Integer[] resultCodes, final OnShellValidateListener validater, final OnShellResultListener listener) {
if(Common.DEBUG)Log.d(TAG, "executeAsync: Starting an async shell execution");
/*
* If someone execute more than one async task after another, and use the same listener,
* we could end up getting the result in the wrong order. We need to make sure that each Thread is started in the correct order.
*/
final Object lock = new Object();
new Thread() {
@Override
public void run() {
Result result = null;
synchronized (lock) {
lock.notifyAll();
}
synchronized(mLock) {
result = Shell.this.execute(commands, resultCodes, validater);
}
listener.onShellResult(result);
}
}.start();
/*
* Do not exit this method, until the Thread is started.
*/
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
/**
* For internal usage
*/
public static void sendBroadcast(String key, Bundle data) {
for (Shell instance : mInstances) {
instance.broadcastReciever(key, data);
}
}
/**
* For internal usage
*/
protected void broadcastReciever(String key, Bundle data) {
for (OnShellBroadcastListener recievers : mBroadcastRecievers) {
recievers.onShellBroadcast(key, data);
}
}
/**
* For internal usage
*/
public void addBroadcastListener(OnShellBroadcastListener listener) {
mBroadcastRecievers.add(listener);
}
/**
* Add a shell connection listener. This callback will be invoked whenever the connection to
* the shell changes.
*
* @param listener
* A {@link OnShellConnectionListener} callback instance
*/
public void addShellConnectionListener(OnShellConnectionListener listener) {
mConnectionRecievers.add(listener);
}
/**
* Remove a shell connection listener from the stack.
*
* @param listener
* A {@link OnShellConnectionListener} callback instance
*/
public void removeShellConnectionListener(OnShellConnectionListener listener) {
mConnectionRecievers.remove(listener);
}
/**
* Check whether or not root was requested for this shell.
*/
public Boolean isRoot() {
return mIsRoot;
}
/**
* Check whether or not a shell connection was established.
*/
public Boolean isConnected() {
return mIsConnected;
}
/**
* Get the current shell execution timeout.
* This is the time in milliseconds from which an execution is killed in case it has stalled.
*/
public Integer getTimeout() {
return mShellTimeout;
}
/**
* Change the shell execution timeout. This should be in milliseconds.
* If this is set to '0', there will be no timeout.
*/
public void setTimeout(Integer timeout) {
if (timeout >= 0) {
mShellTimeout = timeout;
}
}
/**
* Add another result code that represent a successful execution. By default only '0' is used, since
* most shell commands uses '0' for success and '1' for error. But some commands uses different values, like 'cat'
* that uses '130' as success when piping content.
*
* @param resultCode
* The result code to add to the stack
*/
public void addResultCode(Integer resultCode) {
mResultCodes.add(resultCode);
}
/**
* Remove a result code from the stack.
*
* @see Shell#addResultCode(Integer)
*
* @param resultCode
* The result code to remove from the stack
*/
public void removeResultCode(Integer resultCode) {
mResultCodes.remove(resultCode);
}
/**
* Reset the stack containing result codes and set it back to default only containing '0'.
*
* @see Shell#addResultCode(Integer)
*/
public void resetResultCodes() {
mResultCodes.clear();
mResultCodes.add(0);
}
/**
* Close the shell connection using 'exit 0' if possible, or by force and release all data stored in this instance.
*/
public void destroy() {
if (mStream != null) {
mIsConnected = false;
if (mStream.isRunning() || !mStream.isActive()) {
if(Common.DEBUG)Log.d(TAG, "destroy: Destroying the stream");
mStream.destroy();
} else {
if(Common.DEBUG)Log.d(TAG, "destroy: Making a clean exit on the stream");
execute("exit 0");
}
mStream = null;
mInstances.remove(this);
mBroadcastRecievers.clear();
}
}
/**
* Locate whichever toolbox in {@value Common#BINARIES} that supports a specific command.<br /><br />
*
* Example: String("cat") might return String("busybox cat") or String("toolbox cat")
*
* @param bin
* The command to check
*/
public String findCommand(String bin) {
if (!mBinaries.containsKey(bin)) {
for (String toolbox : Common.BINARIES) {
String cmd = toolbox != null && toolbox.length() > 0 ? toolbox + " " + bin : bin;
Result result = execute( cmd + " -h" );
if (result != null) {
String line = result.getLine();
if (!line.endsWith("not found") && !line.endsWith("such tool")) {
mBinaries.put(bin, cmd); break;
}
}
}
}
return mBinaries.get(bin);
}
/**
* Create a new instance of {@link Attempts}
*
* @param command
* The command to convert into multiple attempts
*/
public Attempts createAttempts(String command) {
if (command != null) {
return new Attempts(command);
}
return null;
}
/**
* Open a new RootFW {@link FileReader}. This is the same as {@link FileReader#FileReader(Shell, String)}.
*
* @param file
* Path to the file
*
* @return
* NULL if the file could not be opened
*/
public FileReader getFileReader(String file) {
try {
return new FileReader(this, file);
} catch (FileNotFoundException e) {
return null;
}
}
/**
* Open a new RootFW {@link FileWriter}. This is the same as {@link FileWriter#FileWriter(Shell, String, boolean)}.
*
* @param file
* Path to the file
*
* @param append
* Whether or not to append new content to existing content
*
* @return
* NULL if the file could not be opened
*/
public FileWriter getFileWriter(String file, Boolean append) {
try {
return new FileWriter(this, file, append);
} catch (IOException e) {
return null;
}
}
/**
* Get a new {@link File} instance.
*
* @param file
* Path to the file or directory
*/
public File getFile(String file) {
return new File(this, file);
}
/**
* Get a new {@link Filesystem} instance.
*/
public Filesystem getFilesystem() {
return new Filesystem(this);
}
/**
* Get a new {@link Disk} instance.
*
* @param disk
* Path to a disk, partition or a mount point
*/
public Disk getDisk(String disk) {
return new Disk(this, disk);
}
/**
* Get a new {@link Device} instance.
*/
public Device getDevice() {
return new Device(this);
}
/**
* Get a new {@link Process} instance.
*
* @param process
* The name of the process
*/
public Process getProcess(String process) {
return new Process(this, process);
}
/**
* Get a new {@link Process} instance.
*
* @param pid
* The process id
*/
public Process getProcess(Integer pid) {
return new Process(this, pid);
}
/**
* Get a new {@link Memory} instance.
*/
public Memory getMemory() {
return new Memory(this);
}
/**
* Get a new {@link CompCache} instance.
*/
public CompCache getCompCache() {
return new CompCache(this);
}
/**
* Get a new {@link Swap} instance.
*
* @param device
* The /dev/ swap device
*/
public Swap getSwap(String device) {
return new Swap(this, device);
}
}

View File

@ -0,0 +1,363 @@
/*
* 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 com.spazedog.lib.rootfw4;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import android.util.Log;
/**
* This class opens a connection to the shell and creates a consistent output stream
* that can be read using the {@link OnStreamListener} interface. It also
* contains an input stream that can be used to execute shell commands.
*/
public class ShellStream {
public static final String TAG = Common.TAG + ".ShellStream";
protected Process mConnection;
protected DataOutputStream mStdInput;
protected BufferedReader mStdOutput;
protected Thread mStdOutputWorker;
protected OnStreamListener mListener;
protected final Counter mCounter = new Counter();
protected final Object mLock = new Object();
protected Boolean mIsActive = false;
protected Boolean mIsRoot = false;
protected String mCommandEnd = "EOL:a00c38d8:EOL";
protected static class Counter {
private volatile Integer mCount = 0;
private volatile Object mLock = new Object();
public Integer size() {
synchronized(mLock) {
return mCount;
}
}
public Integer encrease() {
synchronized(mLock) {
return (mCount += 1);
}
}
public Integer decrease() {
synchronized(mLock) {
return mCount > 0 ? (mCount -= 1) : (mCount = 0);
}
}
public void reset() {
synchronized(mLock) {
mCount = 0;
}
}
}
/**
* This interface is used to read the input from the shell.
*/
public static interface OnStreamListener {
/**
* This is called before a command is sent to the shell output stream.
*/
public void onStreamStart();
/**
* This is called on each line from the shell input stream.
*
* @param inputLine
* The current shell input line that is being processed
*/
public void onStreamInput(String outputLine);
/**
* This is called after all shell input has been processed.
*
* @param exitCode
* The exit code returned by the shell
*/
public void onStreamStop(Integer resultCode);
/**
* This is called when the shell connection dies.
* This can either be because a command executed 'exit', or if the method {@link ShellStream#destroy()} was called.
*/
public void onStreamDied();
}
/**
* Connect to a shell and create a consistent I/O stream.
*/
public ShellStream(Boolean requestRoot, OnStreamListener listener) {
try {
if(Common.DEBUG)Log.d(TAG, "Construct: Establishing a new shell stream");
ProcessBuilder builder = new ProcessBuilder(requestRoot ? "su" : "sh");
builder.redirectErrorStream(true);
mIsRoot = requestRoot;
mIsActive = true;
mListener = listener;
mConnection = builder.start();
mStdInput = new DataOutputStream(mConnection.getOutputStream());
mStdOutput = new BufferedReader(new InputStreamReader(mConnection.getInputStream()));
mStdOutputWorker = new Thread() {
@Override
public void run() {
String output = null;
try {
while (mIsActive && (output = mStdOutput.readLine()) != null) {
if (mListener != null && mCounter.size() > 0) {
if (output.contains(mCommandEnd)) {
Integer result = 0;
try {
if (output.startsWith(mCommandEnd)) {
result = Integer.parseInt(output.substring(mCommandEnd.length()+1));
} else {
result = 1;
}
} catch (Throwable e) {
Log.w(TAG, e.getMessage(), e);
}
mListener.onStreamStop(result);
mCounter.decrease();
synchronized(mLock) {
mLock.notifyAll();
}
} else {
mListener.onStreamInput(output);
}
}
}
} catch (IOException e) {
Log.w(TAG, e.getMessage(), e); output = null;
}
if (output == null) {
ShellStream.this.destroy();
}
}
};
mStdOutputWorker.start();
} catch (IOException e) {
Log.w(TAG, e.getMessage(), e); mIsActive = false;
}
}
/**
* Send a command to the shell input stream.<br /><br />
*
* This method is executed asynchronous. If you need to wait until the command finishes,
* then use {@link ShellStream#waitFor()}.
*
* @param command
* The command to send to the shell
*/
public synchronized void execute(final String command) {
final Object lock = new Object();
new Thread() {
@Override
public void run() {
mCounter.encrease();
synchronized(lock) {
lock.notifyAll();
}
synchronized(mLock) {
if (waitFor(0, -1)) {
mListener.onStreamStart();
String input = command + "\n";
input += " echo " + mCommandEnd + " $?\n";
try {
mStdInput.write( input.getBytes() );
/*
* Things often get written to the shell without flush().
* This breaks when using exit, as it some times get destroyed before reaching here.
*/
if (mStdInput != null) {
mStdInput.flush();
}
} catch (IOException e) {
Log.w(TAG, e.getMessage(), e);
}
}
}
}
}.start();
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
}
/**
* Sleeps until the shell is done with a current command and ready for new input.
*
* @see {@link ShellStream#waitFor(Integer)}
*
* @return
* True if the shell connection is OK or false on connection error
*/
public Boolean waitFor() {
return waitFor(0, 0);
}
/**
* Sleeps until the shell is done with a current command and ready for new input,
* or until the specified timeout has expired.<br /><br />
*
* Note that this method keeps track of the order of executions. This means that
* the shell might not be ready, just because this lock was cleared. There might have been
* added more locks after this one was set.
*
* @param timeout
* Timeout in milliseconds
*
* @return
* True if the shell connection is OK or false on connection error
*/
public Boolean waitFor(Integer timeout) {
return waitFor(timeout, 0);
}
/**
* This is an internal method, which is used to change which object to add a lock to.
*/
protected Boolean waitFor(Integer timeout, Integer index) {
Integer counter = mCounter.size()+index;
if (counter > 0) {
Long timeoutMilis = timeout > 0 ? System.currentTimeMillis() + timeout : 0L;
synchronized(mLock) {
while (mCounter.size() > 0 && mIsActive) {
try {
counter -= 1;
mLock.wait(timeout.longValue());
if (timeout > 0 && System.currentTimeMillis() >= timeoutMilis) {
return mCounter.size() == 0 && mIsActive;
} else if (counter <= 0) {
return mIsActive;
}
} catch (InterruptedException e) {
Log.w(TAG, e.getMessage(), e);
}
}
}
}
return mIsActive;
}
/**
* Check whether there is a connection to the shell.
*
* @return
* True if there is a connection or False if not
*/
public Boolean isActive() {
return mIsActive;
}
/**
* Check whether the shell is currently busy processing a command.
*
* @return
* True if the shell is busy or False otherwise
*/
public Boolean isRunning() {
return mCounter.size() > 0;
}
/**
* Check whether or not root was requested for this instance.
*/
public Boolean isRoot() {
return mIsRoot;
}
/**
* Close the shell connection. <br /><br />
*
* This will force close the connection. Use this only when running a consistent command (if {@link ShellStream#isRunning()} returns true).
* When possible, sending the 'exit' command to the shell is a better choice. <br /><br />
*
* This method is executed asynchronous.
*/
public synchronized void destroy() {
if (mStdInput != null) {
mIsActive = false;
mCounter.reset();
try {
mStdInput.close();
mStdInput = null;
} catch (IOException e) {}
mStdOutputWorker.interrupt();
mStdOutputWorker = null;
synchronized (mLock) {
mLock.notifyAll();
}
mListener.onStreamDied();
mListener = null;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4.containers;
import java.util.HashMap;
import java.util.Map;
public abstract class BasicContainer {
private Map<String, Object> mObjects = new HashMap<String, Object>();
public void putObject(String name, Object object) {
mObjects.put(name, object);
}
public Object getObject(String name) {
return mObjects.get(name);
}
}

View File

@ -0,0 +1,414 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4.containers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.text.TextUtils;
/**
* This container is used to store any kind of data. All of the data is located within a String Array, where each index is considered a line.
*/
@SuppressWarnings("unchecked")
public class Data<DATATYPE extends Data<DATATYPE>> extends BasicContainer {
protected String[] mLines;
/**
* This interface is used as the argument for the <code>sort()</code> and <code>assort()</code> methods. It can be used to create custom sorting of the data array.
*/
public static interface DataSorting {
/**
* The method which checks the line and determines whether or not it should be added or removed from the array.
* <br />
* Note that the <code>sort()</code> method will remove the line upon false, whereas <code>assort()</code> will remove it upon true.
*
* @param input
* One line of the data array
*/
public Boolean test(String input);
}
/**
* This interface is used as the argument for the <code>replace()</code> method. It can be used to replace or alter lines in the output.
*/
public static interface DataReplace {
/**
* This method is used to alter the lines in the data array. Each line is parsed to this method, and whatever is returned will replace the current line.
*
* @param input
* One line of the data array
*/
public String replace(String input);
}
/**
* Create a new Data instance.
*
* @param lines
* An array representing the lines of the data
*/
public Data(String[] lines) {
mLines = lines;
}
/**
* This can be used to replace part of a line, or the whole line. It uses the replace() method in the DataSorting interface where custom replacement of a line can be done. It parses the original line as an argument, and requires the new line to be returned.
*
* @param DataSorting
* An instance of the <code>DataSorting</code> class which should handle the line replacement
*
* @return
* This instance
*/
public DATATYPE replace(DataReplace dataReplace) {
if (size() > 0) {
List<String> list = new ArrayList<String>();
for (int i=0; i < mLines.length; i++) {
list.add( dataReplace.replace(mLines[i]) );
}
}
return (DATATYPE) this;
}
/**
* This can be used to replace whole lines based on a contained pattern.
*
* @param contains
* The pattern which the line should contain
*
* @param newLine
* The new line that should be used as a replacement
*
* @return
* This instance
*/
public DATATYPE replace(final String contains, final String newLine) {
return (DATATYPE) replace(new DataReplace() {
@Override
public String replace(String input) {
return input != null && input.contains(contains) ? newLine : input;
}
});
}
/**
* This is used to determine whether or not to remove lines from the data array. Each line will be parsed to the custom <code>DataSorting</code> instance and then removed upon a true return.
*
* @param DataSorting
* An instance of the <code>DataSorting</code> class which should determine whether or not to remove the line
*
* @return
* This instance
*/
public DATATYPE assort(DataSorting test) {
if (size() > 0) {
List<String> list = new ArrayList<String>();
for (int i=0; i < mLines.length; i++) {
if (!test.test(mLines[i])) {
list.add(mLines[i]);
}
}
mLines = list.toArray( new String[list.size()] );
}
return (DATATYPE) this;
}
/**
* This is used to determine whether or not to remove lines from the data array. Each line will be compared to the argument. If the line contains anything from the argument, it will be removed from the data array.
*
* @param contains
* A string to locate within each line to determine whether or not to remove the line
*
* @return
* This instance
*/
public DATATYPE assort(final String contains) {
return (DATATYPE) assort(new DataSorting() {
@Override
public Boolean test(String input) {
return input.contains( contains );
}
});
}
/**
* This is used to determine whether or not to keep lines in the data array. Each line will be parsed to the custom <code>DataSorting</code> instance and then removed upon a false return.
*
* @param DataSorting
* An instance of the <code>DataSorting</code> interface which should determine whether or not to keep the line
*
* @return
* This instance
*/
public DATATYPE sort(DataSorting test) {
if (size() > 0) {
List<String> list = new ArrayList<String>();
for (int i=0; i < mLines.length; i++) {
if (test.test(mLines[i])) {
list.add(mLines[i]);
}
}
mLines = list.toArray( new String[list.size()] );
}
return (DATATYPE) this;
}
/**
* This is used to determine whether or not to keep lines in the data array. Each line will be compared to the argument. If the line contains anything from the argument, it will not be removed from the data array.
*
* @param contains
* A string to locate within each line to determine whether or not to remove the line
*
* @return
* This instance
*/
public DATATYPE sort(final String contains) {
return (DATATYPE) sort(new DataSorting() {
public Boolean test(String input) {
return input.contains( contains );
}
});
}
/**
* @see Data#sort(Integer, Integer)
*/
public DATATYPE sort(Integer start) {
return (DATATYPE) sort(start, mLines.length);
}
/**
* This is used to determine whether or not to keep lines in the data array. The method will keep each index within the <code>start</code> and <code>stop</code> indexes parsed via the arguments.
* <br />
* Note that the method will also except negative values.
*
* <dl>
* <dt><span class="strong">Example 1:</span></dt>
* <dd>Remove the first and last index in the array</dd>
* <dd><code>sort(1, -1)</code></dd>
* </dl>
*
* <dl>
* <dt><span class="strong">Example 2:</span></dt>
* <dd>Only keep the first and last index in the array</dd>
* <dd><code>sort(-1, 1)</code></dd>
* </dl>
*
* @param start
* Where to start
*
* @param stop
* Where to stop
*
* @return
* This instance
*/
public DATATYPE sort(Integer start, Integer stop) {
if (size() > 0) {
List<String> list = new ArrayList<String>();
Integer begin = start < 0 ? (mLines.length + start) : start;
Integer end = stop < 0 ? (mLines.length + stop) : stop;
Integer[] min = null, max = null;
if (begin > end) {
if (end == 0) {
min = new Integer[]{ begin };
max = new Integer[]{ mLines.length };
} else {
min = new Integer[]{ 0, begin };
max = new Integer[]{ end, mLines.length };
}
} else {
min = new Integer[]{ begin };
max = new Integer[]{ end };
}
for (int i=0; i < min.length; i++) {
for (int x=min[i]; x < max[i]; x++) {
list.add(mLines[x]);
}
}
mLines = list.toArray( new String[list.size()] );
}
return (DATATYPE) this;
}
/**
* This is used to determine whether or not to remove lines from the data array. The method will remove each index within the <code>start</code> and <code>stop</code> indexes parsed via the arguments.
* <br />
* Note that the method will also except negative values.
*
* <dl>
* <dt><span class="strong">Example 1:</span></dt>
* <dd>Remove the first and last index in the array</dd>
* <dd><code>sort(-1, 1)</code></dd>
* </dl>
*
* <dl>
* <dt><span class="strong">Example 2:</span></dt>
* <dd>Only keep the first and last index in the array</dd>
* <dd><code>sort(1, -1)</code></dd>
* </dl>
*
* @param start
* Where to start
*
* @param stop
* Where to stop
*
* @return
* This instance
*/
public DATATYPE assort(Integer start, Integer stop) {
return (DATATYPE) sort(stop, start);
}
/**
* @see Data#assort(Integer, Integer)
*/
public DATATYPE assort(Integer start) {
return (DATATYPE) assort(mLines.length, start);
}
/**
* This method will remove all of the empty lines from the data array
*
* @return
* This instance
*/
public DATATYPE trim() {
if (size() > 0) {
List<String> list = new ArrayList<String>();
for (int i=0; i < mLines.length; i++) {
if (mLines[i].trim().length() > 0) {
list.add(mLines[i]);
}
}
mLines = list.toArray( new String[list.size()] );
}
return (DATATYPE) this;
}
/**
* This will return the data array
*
* @return
* The data array
*/
public String[] getArray() {
return mLines;
}
/**
* This will return a string of the data array with added line breakers
*
* @return
* The data array as a string
*/
public String getString() {
return getString("\n");
}
/**
* This will return a string of the data array with custom characters used as line breakers
*
* @param start
* A separator character used to separate each line
*
* @return
* The data array as a string
*/
public String getString(String separater) {
return mLines == null ? null : TextUtils.join(separater, Arrays.asList(mLines));
}
/**
* @return
* The last non-empty line of the data array
*/
public String getLine() {
return getLine(-1, true);
}
/**
* @see Data#getLine(Integer, Boolean)
*/
public String getLine(Integer aLineNumber) {
return getLine(aLineNumber, false);
}
/**
* This will return one specified line of the data array.
* <br />
* Note that this also takes negative number to get a line from the end and up
*
* @param aLineNumber
* The line number to return
*
* @param aSkipEmpty
* Whether or not to include empty lines
*
* @return
* The specified line
*/
public String getLine(Integer aLineNumber, Boolean aSkipEmpty) {
if (size() > 0) {
Integer count = aLineNumber < 0 ? (mLines.length + aLineNumber) : aLineNumber;
while(count >= 0 && count < mLines.length) {
if (!aSkipEmpty || mLines[count].trim().length() > 0) {
return mLines[count].trim();
}
count = aLineNumber < 0 ? (count - 1) : (count + 1);
}
}
return null;
}
/**
* Count the lines in the data array
*
* @return
* The number of lines
*/
public Integer size() {
return mLines == null ? 0 : mLines.length;
}
}

View File

@ -0,0 +1,444 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
import com.spazedog.lib.rootfw4.Common;
import com.spazedog.lib.rootfw4.Shell;
import com.spazedog.lib.rootfw4.Shell.Result;
import com.spazedog.lib.rootfw4.containers.BasicContainer;
public class Device {
public static final String TAG = Common.TAG + ".Device";
protected final static Pattern oPatternPidMatch = Pattern.compile("^[0-9]+$");
protected final static Pattern oPatternSpaceSearch = Pattern.compile("[ \t]+");
protected Shell mShell;
/**
* This is a container class used to store information about a process.
*/
public static class ProcessInfo extends BasicContainer {
private String mPath;
private String mProcess;
private Integer mProcessId;
/**
* @return
* The process path (Could be NULL) as not all processes has a path assigned
*/
public String path() {
return mPath;
}
/**
* @return
* The name of the process
*/
public String name() {
return mProcess;
}
/**
* @return
* The pid of the process
*/
public Integer pid() {
return mProcessId;
}
}
public Device(Shell shell) {
mShell = shell;
}
/**
* Return a list of all active processes.
*/
public ProcessInfo[] getProcessList() {
return getProcessList(null);
}
/**
* Return a list of all active processes which names matches 'pattern'.
* Note that this does not provide an advanced search, it just checks whether or not 'pattern' exists in the process name.
*
* @param pattern
* The pattern to search for
*/
public ProcessInfo[] getProcessList(String pattern) {
String[] files = mShell.getFile("/proc").getList();
if (files != null) {
List<ProcessInfo> processes = new ArrayList<ProcessInfo>();
String process = null;
String path = null;
for (int i=0; i < files.length; i++) {
if (oPatternPidMatch.matcher(files[i]).matches()) {
if ((process = mShell.getFile("/proc/" + files[i] + "/cmdline").readOneLine()) == null) {
if ((process = mShell.getFile("/proc/" + files[i] + "/stat").readOneLine()) != null) {
try {
if (pattern == null || process.contains(pattern)) {
process = oPatternSpaceSearch.split(process.trim())[1];
process = process.substring(1, process.length()-1);
} else {
continue;
}
} catch(Throwable e) { process = null; }
}
} else if (pattern == null || process.contains(pattern)) {
if (process.contains("/")) {
try {
path = process.substring(process.indexOf("/"), process.contains("-") ? process.indexOf("-", process.lastIndexOf("/", process.indexOf("-"))) : process.length());
} catch (Throwable e) { path = null; }
if (!process.startsWith("/")) {
process = process.substring(0, process.indexOf("/"));
} else {
try {
process = process.substring(process.lastIndexOf("/", process.contains("-") ? process.indexOf("-") : process.length())+1, process.contains("-") ? process.indexOf("-", process.lastIndexOf("/", process.indexOf("-"))) : process.length());
} catch (Throwable e) { process = null; }
}
} else if (process.contains("-")) {
process = process.substring(0, process.indexOf("-"));
}
} else {
continue;
}
if (pattern == null || (process != null && process.contains(pattern))) {
ProcessInfo stat = new ProcessInfo();
stat.mPath = path;
stat.mProcess = process;
stat.mProcessId = Integer.parseInt(files[i]);
processes.add(stat);
}
}
}
return processes.toArray( new ProcessInfo[ processes.size() ] );
}
return null;
}
/**
* Reboots the device into the recovery.<br /><br />
*
* This method first tries using the {@link PowerManager}, if that fails it fallbacks on using the reboot command from toolbox.<br /><br />
*
* Note that using the {@link PowerManager} requires your app to optain the 'REBOOT' permission. If you don't want this, just parse NULL as {@link Context}
* and the method will use the fallback. This however is more likely to fail, as many toolbox versions does not support the reboot command.
* And since only the kernel can write to the CBC, we need a native caller to invoke this. So there is no fallback for missing toolbox support when it comes
* to rebooting into the recovery.
*
* @param context
* A {@link Context} or NULL to skip using the {@link PowerManager}
*/
public Boolean rebootRecovery(Context context) {
if (context != null) {
try {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
pm.reboot(null);
/*
* This will never be reached if the reboot is successful
*/
return false;
} catch (Throwable e) {}
}
Result result = mShell.execute("toolbox reboot recovery");
return result != null && result.wasSuccessful();
}
/**
* Invokes a soft reboot on the device (Restart all services) by using a sysrq trigger.
*/
public Boolean rebootSoft() {
Result result = mShell.execute("echo 1 > /proc/sys/kernel/sysrq && echo s > /proc/sysrq-trigger && echo e > /proc/sysrq-trigger");
return result != null && result.wasSuccessful();
}
/**
* Reboots the device.<br /><br />
*
* This method first tries using the reboot command from toolbox.
* But since some toolbox versions does not have this, it further fallbacks on using a sysrq trigger.
*/
public Boolean reboot() {
Result result = mShell.execute("toolbox reboot");
if (result == null || !result.wasSuccessful()) {
result = mShell.execute("echo 1 > /proc/sys/kernel/sysrq && echo s > /proc/sysrq-trigger && echo b > /proc/sysrq-trigger");
}
return result != null && result.wasSuccessful();
}
/**
* Shuts down the device.<br /><br />
*
* This method first tries using the reboot command from toolbox.
* But since some toolbox versions does not have this, it further fallbacks on using a sysrq trigger.
*/
public Boolean shutdown() {
Result result = mShell.execute("toolbox reboot -p");
if (result == null || !result.wasSuccessful()) {
result = mShell.execute("echo 1 > /proc/sys/kernel/sysrq && echo s > /proc/sysrq-trigger && echo o > /proc/sysrq-trigger");
}
return result != null && result.wasSuccessful();
}
/**
* Get a new {@link Process} instance
*
* @param process
* The name of the process
*/
public Process getProcess(String process) {
return new Process(mShell, process);
}
/**
* Get a new {@link Process} instance
*
* @param pid
* The process id
*/
public Process getProcess(Integer pid) {
return new Process(mShell, pid);
}
public static class Process extends Device {
protected Integer mPid;
protected String mProcess;
public Process(Shell shell, String process) {
super(shell);
if (oPatternPidMatch.matcher(process).matches()) {
mPid = Integer.parseInt(process);
} else {
mProcess = process;
}
}
public Process(Shell shell, Integer pid) {
super(shell);
mPid = pid;
}
/**
* Get the pid of the current process.
* If you initialized this object using a process id, this method will return that id.
* Otherwise it will return the first found pid in /proc.
*/
public Integer getPid() {
/*
* A process might have more than one pid. So we never cache a search. If mPid is not null,
* then a specific pid was chosen for this instance, and this is what we should work with.
* But if a process name was chosen, we should never cache the pid as we might one the next one if we kill this process or it dies or reboots.
*/
if (mPid != null) {
return mPid;
}
/*
* Busybox returns 1 both when pidof is not supported and if the process does not exist.
* We need to check if we have some kind of pidof support from either busybox, toolbox or another registered binary.
* If not, we fallback on a /proc search.
*/
String cmd = mShell.findCommand("pidof");
if (cmd != null) {
Result result = mShell.execute(cmd + " '" + mProcess + "'");
String pids = result.getLine();
if (pids != null) {
try {
return Integer.parseInt(oPatternSpaceSearch.split(pids.trim())[0]);
} catch (Throwable e) {
Log.w(TAG, e.getMessage(), e);
}
}
} else {
ProcessInfo[] processes = getProcessList();
if (processes != null) {
for (int i=0; i < processes.length; i++) {
if (mProcess.equals(processes[i].name())) {
return processes[i].pid();
}
}
}
}
return 0;
}
/**
* Get a list of all pid's for this process name.
*/
public Integer[] getPids() {
String name = getName();
String cmd = mShell.findCommand("pidof");
if (cmd != null) {
Result result = mShell.createAttempts(cmd + " '" + name + "'").execute();
if (result != null && result.wasSuccessful()) {
String pids = result.getLine();
if (pids != null) {
String[] parts = oPatternSpaceSearch.split(pids.trim());
Integer[] values = new Integer[ parts.length ];
for (int i=0; i < parts.length; i++) {
try {
values[i] = Integer.parseInt(parts[i]);
} catch(Throwable e) {}
}
return values;
}
}
} else {
ProcessInfo[] processes = getProcessList();
if (name != null && processes != null && processes.length > 0) {
List<Integer> list = new ArrayList<Integer>();
for (int i=0; i < processes.length; i++) {
if (name.equals(processes[i].name())) {
list.add(processes[i].pid());
}
}
return list.toArray( new Integer[ list.size() ] );
}
}
return null;
}
/**
* Get the process name of the current process.
* If you initialized this object using a process name, this method will return that name.
* Otherwise it will locate it in /proc based on the pid.
*/
public String getName() {
if (mProcess != null) {
return mProcess;
}
String process = null;
if ((process = mShell.getFile("/proc/" + mPid + "/cmdline").readOneLine()) == null) {
if ((process = mShell.getFile("/proc/" + mPid + "/stat").readOneLine()) != null) {
try {
process = oPatternSpaceSearch.split(process.trim())[1];
process = process.substring(1, process.length()-1);
} catch(Throwable e) { process = null; }
}
} else if (process.contains("/")) {
if (!process.startsWith("/")) {
process = process.substring(0, process.indexOf("/"));
} else {
try {
process = process.substring(process.lastIndexOf("/", process.contains("-") ? process.indexOf("-") : process.length())+1, process.contains("-") ? process.indexOf("-", process.lastIndexOf("/", process.indexOf("-"))) : process.length());
} catch (Throwable e) { process = null; }
}
} else if (process.contains("-")) {
process = process.substring(0, process.indexOf("-"));
}
return process;
}
/**
* Kill this process.
* If you initialized this object using a pid, only this single process will be killed.
* If you used a process name, all processes with this process name will be killed.
*/
public Boolean kill() {
Result result = null;
if (mPid != null) {
result = mShell.createAttempts("kill -9 '" + mPid + "'").execute();
} else {
result = mShell.createAttempts("killall '" + mProcess + "'").execute();
/*
* Toolbox does not support killall
*/
if (result == null || !result.wasSuccessful()) {
Integer[] pids = getPids();
for (Integer pid : pids) {
result = mShell.createAttempts("kill -9 '" + pid + "'").execute();
if (result == null || !result.wasSuccessful()) {
return false;
}
}
}
}
return result != null && result.wasSuccessful();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,693 @@
/*
* This file is part of the RootFW Project: https://github.com/spazedog/rootfw
*
* Copyright (c) 2015 Daniel Bergløv
*
* RootFW 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 3 of the License, or
* (at your option) any later version.
* RootFW 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 RootFW. If not, see <http://www.gnu.org/licenses/>
*/
package com.spazedog.lib.rootfw4.utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Pattern;
import android.text.TextUtils;
import com.spazedog.lib.rootfw4.Common;
import com.spazedog.lib.rootfw4.Shell;
import com.spazedog.lib.rootfw4.Shell.Result;
import com.spazedog.lib.rootfw4.containers.BasicContainer;
import com.spazedog.lib.rootfw4.utils.File.FileData;
public class Filesystem {
public static final String TAG = Common.TAG + ".Filesystem";
protected final static Pattern oPatternSpaceSearch = Pattern.compile("[ \t]+");
protected final static Pattern oPatternSeparatorSearch = Pattern.compile(",");
protected final static Pattern oPatternPrefixSearch = Pattern.compile("^.*[A-Za-z]$");
protected static MountStat[] oFstabList;
protected static final Object oFstabLock = new Object();
protected Shell mShell;
protected Object mLock = new Object();
/**
* This is a container used to store disk information.
*/
public static class DiskStat extends BasicContainer {
private String mDevice;
private String mLocation;
private Long mSize;
private Long mUsage;
private Long mAvailable;
private Integer mPercentage;
/**
* @return
* Device path
*/
public String device() {
return mDevice;
}
/**
* @return
* Mount location
*/
public String location() {
return mLocation;
}
/**
* @return
* Disk size in bytes
*/
public Long size() {
return mSize;
}
/**
* @return
* Disk usage size in bytes
*/
public Long usage() {
return mUsage;
}
/**
* @return
* Disk available size in bytes
*/
public Long available() {
return mAvailable;
}
/**
* @return
* Disk usage percentage
*/
public Integer percentage() {
return mPercentage;
}
}
/**
* This is a container used to store mount information.
*/
public static class MountStat extends BasicContainer {
private String mDevice;
private String mLocation;
private String mFstype;
private String[] mOptions;
/**
* @return
* The device path
*/
public String device() {
return mDevice;
}
/**
* @return
* The mount location
*/
public String location() {
return mLocation;
}
/**
* @return
* The device file system type
*/
public String fstype() {
return mFstype;
}
/**
* @return
* The options used at mount time
*/
public String[] options() {
return mOptions;
}
}
public Filesystem(Shell shell) {
mShell = shell;
}
/**
* Just like {@link #getMountList} this will provide a list of mount points and disks. The difference is that this list will not be of
* all the currently mounted partitions, but a list of all defined mount point in each fstab and init.*.rc file on the device.
* <br /><br />
* It can be useful in situations where a file system might have been moved by a script, and you need the original defined location.
* Or perhaps you need the original device of a specific mount location.
*
* @return
* An array of {@link MountStat} objects
*/
public MountStat[] getFsList() {
synchronized(oFstabLock) {
if (oFstabList == null) {
Result result = mShell.execute("for DIR in /fstab.* /fstab /init.*.rc /init.rc; do echo $DIR; done");
if (result != null && result.wasSuccessful()) {
Set<String> cache = new HashSet<String>();
List<MountStat> list = new ArrayList<MountStat>();
String[] dirs = result.trim().getArray();
for (int i=0; i < dirs.length; i++) {
if (!Common.isEmulator() && dirs[i].contains("goldfish")) {
continue;
}
Boolean isFstab = dirs[i].contains("fstab");
FileData data = mShell.getFile(dirs[i]).readMatch( (isFstab ? "/dev/" : "mount "), false );
if (data != null) {
String[] lines = data.assort("#").getArray();
if (lines != null) {
for (int x=0; x < lines.length; x++) {
try {
String[] parts = oPatternSpaceSearch.split(lines[x].trim(), 5);
String options = isFstab || parts.length > 4 ? parts[ isFstab ? 3 : 4 ].replaceAll(",", " ") : "";
if (parts.length > 3 && !cache.contains(parts[ isFstab ? 1 : 3 ])) {
if (!isFstab && parts[2].contains("mtd@")) {
FileData mtd = mShell.getFile("/proc/mtd").readMatch( ("\"" + parts[2].substring(4) + "\""), false );
if (mtd != null && mtd.size() > 0) {
parts[2] = "/dev/block/mtdblock" + mtd.getLine().substring(3, mtd.getLine().indexOf(":"));
}
} else if (!isFstab && parts[2].contains("loop@")) {
parts[2] = parts[2].substring(5);
options += " loop";
}
MountStat stat = new MountStat();
stat.mDevice = parts[ isFstab ? 0 : 2 ];
stat.mFstype = parts[ isFstab ? 2 : 1 ];
stat.mLocation = parts[ isFstab ? 1 : 3 ];
stat.mOptions = oPatternSpaceSearch.split(options);
list.add(stat);
cache.add(parts[ isFstab ? 1 : 3 ]);
}
} catch(Throwable e) {}
}
}
}
}
oFstabList = list.toArray( new MountStat[ list.size() ] );
}
}
return oFstabList;
}
}
/**
* This will return a list of all currently mounted file systems, with information like
* device path, mount location, file system type and mount options.
*
* @return
* An array of {@link MountStat} objects
*/
public MountStat[] getMountList() {
FileData data = mShell.getFile("/proc/mounts").read();
if (data != null) {
String[] lines = data.trim().getArray();
MountStat[] list = new MountStat[ lines.length ];
for (int i=0; i < lines.length; i++) {
try {
String[] parts = oPatternSpaceSearch.split(lines[i].trim());
list[i] = new MountStat();
list[i].mDevice = parts[0];
list[i].mFstype = parts[2];
list[i].mLocation = parts[1];
list[i].mOptions = oPatternSeparatorSearch.split(parts[3]);
} catch(Throwable e) {}
}
return list;
}
return null;
}
/**
* Get an instance of the {@link Disk} class.
*
* @param disk
* The location to the disk, partition or folder
*/
public Disk getDisk(String disk) {
return new Disk(mShell, disk);
}
public static class Disk extends Filesystem {
protected File mFile;
public Disk(Shell shell, String disk) {
super(shell);
mFile = shell.getFile(disk);
}
/**
* This is a short method for adding additional options to a mount location or device.
* For an example, parsing remount instructions.
*
* @see #mount(String, String, String[])
*
* @param options
* A string array containing all of the mount options to parse
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean mount(String[] options) {
return mount(null, null, options);
}
/**
* This is a short method for attaching a device or folder to a location, without any options or file system type specifics.
*
* @see #mount(String, String, String[])
*
* @param location
* The location where the device or folder should be attached to
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean mount(String location) {
return mount(location, null, null);
}
/**
* This is a short method for attaching a device or folder to a location, without any file system type specifics.
*
* @see #mount(String, String, String[])
*
* @param location
* The location where the device or folder should be attached to
*
* @param options
* A string array containing all of the mount options to parse
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean mount(String location, String[] options) {
return mount(location, null, options);
}
/**
* This is a short method for attaching a device or folder to a location, without any options.
*
* @see #mount(String, String, String[])
*
* @param location
* The location where the device or folder should be attached to
*
* @param type
* The file system type to mount a device as
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean mount(String location, String type) {
return mount(location, type, null);
}
/**
* This is used for attaching a device or folder to a location,
* or to change any mount options on a current mounted file system.
* <br />
* Note that if the device parsed to the constructor {@link #Disk(Shell, String)}
* is a folder, this method will use the <code>--bind</code> option to attach it to the location. Also note that when attaching folders to a location,
* the <code>type</code> and <code>options</code> arguments will not be used and should just be parsed as <code>NULL</code>.
*
* @param location
* The location where the device or folder should be attached to
*
* @param type
* The file system type to mount a device as
*
* @param options
* A string array containing all of the mount options to parse
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean mount(String location, String type, String[] options) {
String cmd = location != null && mFile.isDirectory() ?
"mount --bind '" + mFile.getAbsolutePath() + "' '" + location + "'" :
"mount" + (type != null ? " -t '" + type + "'" : "") + (options != null ? " -o '" + (location == null ? "remount," : "") + TextUtils.join(",", Arrays.asList(options)) + "'" : "") + " '" + mFile.getAbsolutePath() + "'" + (location != null ? " '" + location + "'" : "");
/*
* On some devices, some partitions has been made read-only by writing to the block device ioctls.
* This means that even mounting them as read/write will not work by itself, we need to change the ioctls as well.
*/
if (options != null && !"/".equals(mFile.getAbsolutePath())) {
for (String option : options) {
if ("rw".equals(option)) {
String blockdevice = null;
if (mFile.isDirectory()) {
MountStat stat = getMountDetails();
if (stat != null) {
blockdevice = stat.device();
} else if ((stat = getFsDetails()) != null) {
blockdevice = stat.device();
}
} else {
blockdevice = mFile.getAbsolutePath();
}
if (blockdevice != null && blockdevice.startsWith("/dev/")) {
mShell.createAttempts("blockdev --setrw '" + blockdevice + "' 2> /dev/null").execute();
}
break;
}
}
}
Result result = mShell.createAttempts(cmd).execute();
return result != null && result.wasSuccessful();
}
/**
* This method is used to remove an attachment of a device or folder (unmount).
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean unmount() {
String[] commands = new String[]{"umount '" + mFile.getAbsolutePath() + "'", "umount -f '" + mFile.getAbsolutePath() + "'"};
for (String command : commands) {
Result result = mShell.createAttempts(command).execute();
if (result != null && result.wasSuccessful()) {
return true;
}
}
return false;
}
/**
* This method is used to move a mount location to another location.
*
* @return
* <code>True</code> on success, <code>False</code> otherwise
*/
public Boolean move(String destination) {
Result result = mShell.createAttempts("mount --move '" + mFile.getAbsolutePath() + "' '" + destination + "'").execute();
if (result == null || !result.wasSuccessful()) {
/*
* Not all toolbox versions support moving mount points.
* So in these cases, we fallback to a manual unmount/remount.
*/
MountStat stat = getMountDetails();
if (stat != null && unmount()) {
return getDisk(stat.device()).mount(stat.location(), stat.fstype(), stat.options());
}
}
return result != null && result.wasSuccessful();
}
/**
* This is used to check whether the current device or folder is attached to a location (Mounted).
*
* @return
* <code>True</code> if mounted, <code>False</code> otherwise
*/
public Boolean isMounted() {
return getMountDetails() != null;
}
/**
* This is used to check if a mounted file system was mounted with a specific mount option.
* Note that options like <code>mode=xxxx</code> can also be checked by just parsing <code>mode</code> as the argument.
*
* @param option
* The name of the option to find
*
* @return
* <code>True</code> if the options was used to attach the device, <code>False</code> otherwise
*/
public Boolean hasOption(String option) {
MountStat stat = getMountDetails();
if (stat != null) {
String[] options = stat.options();
if (options != null && options.length > 0) {
for (int i=0; i < options.length; i++) {
if (options[i].equals(option) || options[i].startsWith(option + "=")) {
return true;
}
}
}
}
return false;
}
/**
* This can be used to get the value of a specific mount option that was used to attach the file system.
* Note that options like <code>noexec</code>, <code>nosuid</code> and <code>nodev</code> does not have any values and will return <code>NULL</code>.
* This method is used to get values from options like <code>gid=xxxx</code>, <code>mode=xxxx</code> and <code>size=xxxx</code> where <code>xxxx</code> is the value.
*
* @param option
* The name of the option to find
*
* @return
* <code>True</code> if the options was used to attach the device, <code>False</code> otherwise
*/
public String getOption(String option) {
MountStat stat = getMountDetails();
if (stat != null) {
String[] options = stat.options();
if (options != null && options.length > 0) {
for (int i=0; i < options.length; i++) {
if (options[i].startsWith(option + "=")) {
return options[i].substring( options[i].indexOf("=")+1 );
}
}
}
}
return null;
}
/**
* This is the same as {@link #getMountList()},
* only this method will just return the mount information for this specific device or mount location.
*
* @return
* A single {@link MountStat} object
*/
public MountStat getMountDetails() {
MountStat[] list = getMountList();
if (list != null) {
String path = mFile.getAbsolutePath();
if (!mFile.isDirectory()) {
for (int i=0; i < list.length; i++) {
if (list[i].device().equals(path)) {
return list[i];
}
}
} else {
do {
for (int i=0; i < list.length; i++) {
if (list[i].location().equals(path)) {
return list[i];
}
}
} while (path.lastIndexOf("/") > 0 && !(path = path.substring(0, path.lastIndexOf("/"))).equals(""));
}
}
return null;
}
/**
* This is the same as {@link #getFsList()},
* only this method will just return the mount information for this specific device or mount location.
*
* @return
* A single {@link MountStat} object
*/
public MountStat getFsDetails() {
MountStat[] list = getFsList();
if (list != null) {
String path = mFile.getAbsolutePath();
if (!mFile.isDirectory()) {
for (int i=0; i < list.length; i++) {
if (list[i].device().equals(path)) {
return list[i];
}
}
} else {
do {
for (int i=0; i < list.length; i++) {
if (list[i].location().equals(path)) {
return list[i];
}
}
} while (path.lastIndexOf("/") > 0 && !(path = path.substring(0, path.lastIndexOf("/"))).equals(""));
}
}
return null;
}
/**
* Like with {@link #getMountList()}, this will also return information like device path and mount location.
* However, it will not return information like file system type or mount options, but instead
* information about the disk size, remaining bytes, used bytes and usage percentage.
*
* @return
* A single {@link DiskStat} object
*/
public DiskStat getDiskDetails() {
String[] commands = new String[]{"df -k '" + mFile.getAbsolutePath() + "'", "df '" + mFile.getAbsolutePath() + "'"};
for (String command : commands) {
Result result = mShell.createAttempts(command).execute();
if (result != null && result.wasSuccessful() && result.size() > 1) {
/* Depending on how long the line is, the df command some times breaks a line into two */
String[] parts = oPatternSpaceSearch.split(result.sort(1).trim().getString(" ").trim());
/*
* Any 'df' output, no mater which toolbox or busybox version, should contain at least
* 'device or mount location', 'size', 'used' and 'available'
*/
if (parts.length > 3) {
String pDevice=null, pLocation=null, prefix, prefixList[] = {"k", "m", "g", "t"};
Integer pPercentage=null;
Long pUsage, pSize, pRemaining;
Double[] pUsageSections = new Double[3];
if (parts.length > 5) {
/* Busybox output */
pDevice = parts[0];
pLocation = parts[5];
pPercentage = Integer.parseInt(parts[4].substring(0, parts[4].length()-1));
} else {
/* Toolbox output */
/* Depending on Toolbox version, index 0 can be both the device or the mount location */
MountStat stat = getMountDetails();
if (stat != null) {
pDevice = stat.device();
pLocation = stat.location();
}
}
/* Make sure that the sizes of usage, capacity etc does not have a prefix. Not all toolbox and busybox versions supports the '-k' argument,
* and some does not produce an error when parsed */
for (int i=1; i < 4; i++) {
if (i < parts.length) {
if (oPatternPrefixSearch.matcher(parts[i]).matches()) {
pUsageSections[i-1] = Double.parseDouble( parts[i].substring(0, parts[i].length()-1) );
prefix = parts[i].substring(parts[i].length()-1).toLowerCase(Locale.US);
for (int x=0; x < prefixList.length; x++) {
pUsageSections[i-1] = pUsageSections[i-1] * 1024D;
if (prefixList[x].equals(prefix)) {
break;
}
}
} else {
pUsageSections[i-1] = Double.parseDouble(parts[i]) * 1024D;
}
} else {
pUsageSections[i-1] = 0D;
}
}
pSize = pUsageSections[0].longValue();
pUsage = pUsageSections[1].longValue();
pRemaining = pUsageSections[2].longValue();
if (pPercentage == null) {
/* You cannot divide by zero */
pPercentage = pSize != 0 ? ((Long) ((pUsage * 100L) / pSize)).intValue() : 0;
}
DiskStat info = new DiskStat();
info.mDevice = pDevice;
info.mLocation = pLocation;
info.mSize = pSize;
info.mUsage = pUsage;
info.mAvailable = pRemaining;
info.mPercentage = pPercentage;
return info;
}
}
}
return null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More