Version 1.13

This commit is contained in:
Vlad Mihalachi
2014-10-18 15:20:23 +02:00
parent bf4dcd51b8
commit 4b16119b53
98 changed files with 6678 additions and 3057 deletions

174
.idea/codeStyleSettings.xml generated Normal file
View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</rules>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
</component>
</project>

View File

@ -20,12 +20,12 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
applicationId "com.maskyn.fileeditorpro"
minSdkVersion 10
minSdkVersion 11
targetSdkVersion 19
versionCode 29
versionName "1.12"
@ -41,6 +41,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {

View File

@ -49,7 +49,7 @@
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/AppTheme.Light.Editor">
android:theme="@style/AppThemeEditorDark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -79,32 +79,12 @@
<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=".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=".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">
android:theme="@style/AppThemeBaseLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".HomeActivity" />

View File

@ -38,14 +38,14 @@ repositories {
android {
compileSdkVersion 19
buildToolsVersion "19.1.0"
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
applicationId "com.maskyn.fileeditor"
minSdkVersion 10
minSdkVersion 11
targetSdkVersion 19
versionCode 29
versionName "1.12"
versionCode 30
versionName "1.13"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
@ -57,6 +57,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
@ -64,5 +71,4 @@ dependencies {
compile project(':libraries:sharedCode')
compile 'com.google.android.gms:play-services:5.0.89'
compile 'com.crashlytics.android:crashlytics:1.+'
}

View File

@ -55,7 +55,7 @@
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/AppTheme.Light.Editor">
android:theme="@style/AppThemeEditorDark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -85,32 +85,12 @@
<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=".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=".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">
android:theme="@style/AppThemeBaseLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".HomeActivity" />

View File

@ -22,8 +22,10 @@ package com.maskyn.fileeditor;
import android.os.Bundle;
import com.crashlytics.android.Crashlytics;
import sharedcode.turboeditor.activity.BaseHomeActivity;
import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.util.ProCheckUtils;
public class HomeActivity extends BaseHomeActivity {
@ -36,11 +38,13 @@ public class HomeActivity extends BaseHomeActivity {
if(PreferenceHelper.getSendErrorReports(this))
Crashlytics.start(this);
// setup the ads
if(!ProCheckUtils.isPro(this))
adsHelper = new AdsHelper(this);
}
@Override
public void displayInterstitial() {
if(adsHelper != null && !ProCheckUtils.isPro(this))
adsHelper.displayInterstitial();
}
}

View File

@ -7,54 +7,18 @@
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\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-9b612f0cb16e63277808158fe971bb4f40c98d29.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\com.github.gabrielemariotti.changeloglib\library\1.5.1\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="74a89f0f8b56d9f11d70b8d8134cf4109f4797dc"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\classes-579992e349b2c32e50f8907b9538ad0aa6df57b4.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\com.google.android.gms\play-services\5.0.89\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="d71573c9c5ea98a8db47ad6ff993a63d492b3bfa"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-30cc9565ecef1e8ae8577530d7ddd41993d192d7.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="f449c5674e0427708d4fb17c4a2ec74c267e74d6"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\juniversalchardet-1.0.3-sources-436707958bb47977373cb4a2d842cdbf635fd840.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3-sources.jar"
jumboMode="false"
revision="19.1.0"
sha1="77979eaa98f90806f984155f44f63cc1fb60ac25"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\library-2.4.0-6b043a8574fb97c742ca3065362b2a29e8f870bc.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\com.nineoldandroids\library\2.4.0\e9b63380f3a242dbdbf103a2355ad7e43bad17cb\library-2.4.0.jar"
jumboMode="false"
revision="19.1.0"
sha1="e9b63380f3a242dbdbf103a2355ad7e43bad17cb"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\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="19.1.0"
sha1="0e821eafa1bf489a26bdb71f95078c26785b37a1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-bcfe21eb1248db73c27c811996e28274cf39b024.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="fd9b2146c585dd1d2bebf34947ffe2dbcc017c51"/>
<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"
@ -67,12 +31,6 @@
jumboMode="false"
revision="20.0.0"
sha1="2f3117da0016b1126fafe7fb332a45d2f910d76c"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-82d5b6cab7f16bad663de7c7008673037efb0e1b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="2e2f6526f3c5fb34230d14e52bdc24addb67ea9f"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-sources-58cfedaebe3b94ec0eaa2ede4e66aae8dbe309b0.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3-sources.jar"
@ -80,35 +38,35 @@
revision="19.1.0"
sha1="77979eaa98f90806f984155f44f63cc1fb60ac25"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-7a329e7f23515542bf02d96773d08da1d2333a7b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-9db20cdcb8ae72104757d81297c98978c65bd91b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
jumboMode="false"
revision="19.1.0"
sha1="2e2f6526f3c5fb34230d14e52bdc24addb67ea9f"/>
revision="20.0.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-sources-436707958bb47977373cb4a2d842cdbf635fd840.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3-sources.jar"
jumboMode="false"
revision="20.0.0"
sha1="77979eaa98f90806f984155f44f63cc1fb60ac25"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-9b612f0cb16e63277808158fe971bb4f40c98d29.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\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\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-cdb6170a1d0bff9e575ad3ffbf525f5448b9edfc.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\org.solovyev.android\checkout\0.5.1\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="365d5d82a77aaad1aa83381e97480593d531ac0e"/>
<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\turbo-editor\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="19.1.0"
sha1="587da9a481ffd341d2fa091704489b89845ddacd"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\support-annotations-20.0.0-be727b9c9ce08c6ee055559b9506b675c13db989.jar"
jar="C:\Users\Vlad\AppData\Local\Android\android-sdk\extras\android\m2repository\com\android\support\support-annotations\20.0.0\support-annotations-20.0.0.jar"
jumboMode="false"
revision="20.0.0"
sha1="9d9013e9ff35fc3756411e62873c363c70c638fa"/>
<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"
@ -122,59 +80,29 @@
revision="20.0.0"
sha1="0e821eafa1bf489a26bdb71f95078c26785b37a1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-bcfe21eb1248db73c27c811996e28274cf39b024.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-sources-58cfedaebe3b94ec0eaa2ede4e66aae8dbe309b0.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3-sources.jar"
jumboMode="false"
revision="20.0.0"
sha1="e7a26359f635a1353de0aa8492f56f1f47c42c1a"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-82d5b6cab7f16bad663de7c7008673037efb0e1b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="c9508d53a0ddc7399493e4c7ad474718c5ed196e"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-b074fd82146b96b10c95466b0162b8da83a6ec6e.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\org.solovyev.android\checkout\0.5.1\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="365d5d82a77aaad1aa83381e97480593d531ac0e"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\classes-515446996fed08836a9331ef47b508a0383ffa22.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="cb3d22565863773944a8c15de408e864e34d6da1"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\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="19.1.0"
sha1="85f201b380937e61a9dce6ca90ccf6872abbfb67"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\juniversalchardet-1.0.3-9db20cdcb8ae72104757d81297c98978c65bd91b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
jumboMode="false"
revision="19.1.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
sha1="77979eaa98f90806f984155f44f63cc1fb60ac25"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-65b2b356e3f2da4b67e00aba70923d6321852204.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
jumboMode="false"
revision="20.0.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\classes-91de6979bb6be1b46cde32b462c23831eadd01d4.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\com.github.gabrielemariotti.changeloglib\library\1.5.1\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="74a89f0f8b56d9f11d70b8d8134cf4109f4797dc"/>
<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\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-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"
@ -182,11 +110,107 @@
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"
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="85f201b380937e61a9dce6ca90ccf6872abbfb67"/>
sha1="cdecd8167dfb75d5785decb911fc4516445dd6a6"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\library-2.4.0-6b043a8574fb97c742ca3065362b2a29e8f870bc.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\com.nineoldandroids\library\2.4.0\e9b63380f3a242dbdbf103a2355ad7e43bad17cb\library-2.4.0.jar"
jumboMode="false"
revision="20.0.0"
sha1="e9b63380f3a242dbdbf103a2355ad7e43bad17cb"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\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="19.1.0"
sha1="a18ff12a9ab5ae52fd30d42f134517997568231e"/>
<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\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-30cc9565ecef1e8ae8577530d7ddd41993d192d7.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="cb3d22565863773944a8c15de408e864e34d6da1"/>
<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\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-91de6979bb6be1b46cde32b462c23831eadd01d4.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\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\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-30cc9565ecef1e8ae8577530d7ddd41993d192d7.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="85e7b55f468c5591965a5171179d3858b46822a9"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-515446996fed08836a9331ef47b508a0383ffa22.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="85e7b55f468c5591965a5171179d3858b46822a9"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-bcfe21eb1248db73c27c811996e28274cf39b024.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="8203f36efafa2a6ef438b83fc86b9bdc469e7368"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\commons-lang3-3.1-84728078c80f2c8637e0c3fe426ad61433c75bb6.jar"
jar="C:\Users\Vlad\.gradle\caches\modules-2\files-2.1\org.apache.commons\commons-lang3\3.1\905075e6c80f206bbe6cf1e809d2caa69f420c76\commons-lang3-3.1.jar"
jumboMode="false"
revision="20.0.0"
sha1="905075e6c80f206bbe6cf1e809d2caa69f420c76"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-82d5b6cab7f16bad663de7c7008673037efb0e1b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="2e2f6526f3c5fb34230d14e52bdc24addb67ea9f"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\classes-4dd6beddde59ff8bd6c22bd0d65ac974f7e489d2.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="84a25d78b651ad8b1fb73e463fde4d34a4e79ca6"/>
<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\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-bcfe21eb1248db73c27c811996e28274cf39b024.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="84a25d78b651ad8b1fb73e463fde4d34a4e79ca6"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-82d5b6cab7f16bad663de7c7008673037efb0e1b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="df1a8540495d6724b8582db31fcdd49b04d8b707"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-7a329e7f23515542bf02d96773d08da1d2333a7b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\FloatingActionButton\unspecified\classes.jar"
jumboMode="false"
revision="20.0.0"
sha1="df1a8540495d6724b8582db31fcdd49b04d8b707"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\juniversalchardet-1.0.3-65b2b356e3f2da4b67e00aba70923d6321852204.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\libs\juniversalchardet-1.0.3.jar"
@ -199,42 +223,6 @@
jumboMode="false"
revision="20.0.0"
sha1="591d72211acc0b909b79c840e0b3ed9a0982d807"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\classes-e7e86cab56b519ef20b4d01792717ffcddcc577b.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\com.android.support\support-v4\20.0.0\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="8694693636ab36910c8100ce527c258ff3ebece0"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-4dd6beddde59ff8bd6c22bd0d65ac974f7e489d2.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\turbo-editor.libraries\sharedCode\unspecified\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="7bc7ded1dbd6619408b58e2184aad20bb75533d5"/>
<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\turbo-editor\app-pro\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="19.1.0"
sha1="a18ff12a9ab5ae52fd30d42f134517997568231e"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\release\internal_impl-20.0.0-2cc5dec0eb9c4e1671dc84ade9411d50c227e9ae.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\com.android.support\support-v4\20.0.0\libs\internal_impl-20.0.0.jar"
jumboMode="false"
revision="19.1.0"
sha1="e40b3078e3fbd353c9f44da8df25a8fc0078209d"/>
<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"
@ -248,11 +236,11 @@
revision="20.0.0"
sha1="d71573c9c5ea98a8db47ad6ff993a63d492b3bfa"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\pre-dexed\debug\classes-30cc9565ecef1e8ae8577530d7ddd41993d192d7.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\build\intermediates\exploded-aar\turbo-editor.libraries\RootCommands\unspecified\classes.jar"
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\pre-dexed\debug\classes-579992e349b2c32e50f8907b9538ad0aa6df57b4.jar"
jar="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app\build\intermediates\exploded-aar\com.google.android.gms\play-services\5.0.89\classes.jar"
jumboMode="false"
revision="19.1.0"
sha1="cb3d22565863773944a8c15de408e864e34d6da1"/>
revision="20.0.0"
sha1="d71573c9c5ea98a8db47ad6ff993a63d492b3bfa"/>
<item
dex="C:\Users\Vlad\Documents\AndroidStudioProjects\turbo-editor\app-pro\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"

Binary file not shown.

View File

@ -1,7 +1,7 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
applicationId 'com.faizmalkani.floatingactionbutton'
@ -26,5 +26,4 @@ android {
dependencies {
compile 'com.nineoldandroids:library:2.4.0'
}

View File

@ -15,8 +15,8 @@ dependencies {
}
android {
compileSdkVersion 19
buildToolsVersion '19.1.0'
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
minSdkVersion 7

View File

@ -24,12 +24,12 @@ android {
disable 'MissingTranslation', 'ExtraTranslation'
}
compileSdkVersion 19
buildToolsVersion "19.1.0"
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
applicationId "sharedcode.turboeditor"
minSdkVersion 10
minSdkVersion 11
targetSdkVersion 19
versionCode 1
versionName "1.0"
@ -45,6 +45,15 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
}
dependencies {
@ -55,9 +64,10 @@ dependencies {
exclude module: 'support-v4'
}
// compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
compile 'org.apache.commons:commons-lang3:3.1'
compile files('libs/juniversalchardet-1.0.3.jar')
compile "com.android.support:support-v4:19.0.+"
compile 'com.android.support:support-v4:19.0.+'
compile 'com.github.gabrielemariotti.changeloglib:library:1.5.1'
compile 'commons-io:commons-io:2.4'
compile 'org.solovyev.android:checkout:0.5.1@aar'
compile 'com.android.support:support-annotations:20.0.0'
}

View File

@ -30,19 +30,14 @@ import android.widget.ListView;
import android.widget.TextView;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.util.ThemeUtils;
public class LicensesActivity extends Activity implements AdapterView.OnItemClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
boolean light = PreferenceHelper.getLightTheme(this);
if (light) {
setTheme(R.style.AppTheme_Light);
} else {
setTheme(R.style.AppTheme_Dark);
}
ThemeUtils.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_licenses);

View File

@ -1,238 +0,0 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.activity;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.TextView;
import com.android.vending.billing.IInAppBillingService;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.checkout.ActivityCheckout;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.Inventory;
import org.solovyev.android.checkout.Products;
import java.util.ArrayList;
import java.util.Arrays;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.util.AppInfoHelper;
import sharedcode.turboeditor.util.Constants;
import sharedcode.turboeditor.util.ProCheckUtils;
public class PreferenceAbout extends Activity {
@Override
public void onCreate(final Bundle savedInstanceState) {
setTheme(R.style.AppTheme_Dark);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
TextView changeLogText = (TextView) findViewById(R.id.changelog_text);
TextView proVersionText = (TextView) findViewById(R.id.pro_version_text);
try {
changeLogText.setText(String.format(getString(R.string.app_version), getPackageManager().getPackageInfo(getPackageName(), 0).versionName));
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
proVersionText.setText(ProCheckUtils.isPro(getBaseContext()) ? getString(R.string.donate) : getString(R.string.pro_version));
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
}
IInAppBillingService mService;
ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
}
@Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
}
};
@Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
public void OpenPlayStore(View view) {
try {
if (Constants.FOR_AMAZON) {
String url = "amzn://apps/android?p=com.maskyn.fileeditor";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pub:Maskyn"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
} catch (Exception e) {
}
}
public void GoToProVersion(View view) {
try {
if(ProCheckUtils.isPro(getBaseContext())) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=26VWS2TSAMUJA"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else {
//startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditorpro"))
// .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
"fileeditor.proversion", "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
}
} catch (Exception e) {
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
//alert("You have bought the " + sku + ". Excellent choice,
// adventurer!");
}
catch (JSONException e) {
//alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
public void OpenGithub(View view) {
String url = "http://github.com/vmihalachi/TurboEditor";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public void SendFeedback(View view) {
String url = "http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public void SendMail(View view) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_EMAIL, new String[]{"maskyngames@gmail.com"});
i.putExtra(Intent.EXTRA_SUBJECT, AppInfoHelper.getApplicationName(getBaseContext()) + " " + AppInfoHelper.getCurrentVersion(getBaseContext()));
i.putExtra(Intent.EXTRA_TEXT, "");
try {
startActivity(Intent.createChooser(i, getString(R.string.nome_app_turbo_editor)));
} catch (android.content.ActivityNotFoundException ex) {
}
}
public void OpenTranslatePage(View view) {
String url = "http://crowdin.net/project/turbo-client";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
public void OpenGooglePlusCommunity(View view) {
String url = "http://plus.google.com/u/0/communities/111974095419108178946";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
/*void setupClickablePreferences() {
final Preference email = findPreference("aboutactivity_authoremail"),
changelog = findPreference("aboutactivity_changelog"),
open_source_licenses = findPreference("aboutactivity_open_source_licenses"),
market = findPreference("aboutactivity_authormarket");
if (email != null) {
email.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
return false;
}
});
}
if (changelog != null) {
changelog.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
ChangelogDialogFragment.showChangeLogDialog(getFragmentManager());
return false;
}
});
}
if (open_source_licenses != null) {
open_source_licenses.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
startActivity(new Intent(PreferenceAbout.this, LicensesActivity.class));
return false;
}
});
}
if (market != null) {
market.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
return false;
}
});
}
}*/
}

View File

@ -24,8 +24,6 @@ import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.widget.PopupMenu;
import android.widget.SearchView;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
@ -33,6 +31,8 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.Filter;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
@ -54,13 +54,14 @@ import java.util.concurrent.TimeoutException;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.adapter.AdapterDetailedList;
import sharedcode.turboeditor.fragment.EditDialogFragment;
import sharedcode.turboeditor.fragment.EditTextDialog;
import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.root.RootUtils;
import sharedcode.turboeditor.util.AlphanumComparator;
import sharedcode.turboeditor.util.Constants;
import sharedcode.turboeditor.util.RootUtils;
import sharedcode.turboeditor.util.Build;
import sharedcode.turboeditor.util.ThemeUtils;
public class SelectFileActivity extends Activity implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, EditDialogFragment.EditDialogListener {
public class SelectFileActivity extends Activity implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, EditTextDialog.EditDialogListener {
private String currentFolder = PreferenceHelper.SD_CARD_ROOT;
private ListView listView;
private boolean wantAFile = true;
@ -73,12 +74,7 @@ public class SelectFileActivity extends Activity implements SearchView.OnQueryTe
protected void onCreate(Bundle savedInstanceState) {
boolean light = PreferenceHelper.getLightTheme(this);
if (light) {
setTheme(R.style.AppTheme_Light);
} else {
setTheme(R.style.AppTheme_Dark);
}
ThemeUtils.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_file);
@ -105,14 +101,14 @@ public class SelectFileActivity extends Activity implements SearchView.OnQueryTe
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
final EditDialogFragment dialogFrag;
final EditTextDialog dialogFrag;
int i = item.getItemId();
if (i == R.id.im_new_file) {
dialogFrag = EditDialogFragment.newInstance(EditDialogFragment.Actions.NewFile);
dialogFrag = EditTextDialog.newInstance(EditTextDialog.Actions.NewFile);
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
return true;
} else if (i == R.id.im_new_folder) {
dialogFrag = EditDialogFragment.newInstance(EditDialogFragment.Actions.NewFolder);
dialogFrag = EditTextDialog.newInstance(EditTextDialog.Actions.NewFolder);
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
return true;
} else {
@ -261,11 +257,11 @@ public class SelectFileActivity extends Activity implements SearchView.OnQueryTe
@Override
public void onFinishEditDialog(final String inputText, final String hint, final EditDialogFragment.Actions actions) {
if (actions == EditDialogFragment.Actions.NewFile && !TextUtils.isEmpty(inputText)) {
public void onFinishEditDialog(final String inputText, final String hint, final EditTextDialog.Actions actions) {
if (actions == EditTextDialog.Actions.NewFile && !TextUtils.isEmpty(inputText)) {
File file = new File(currentFolder, inputText);
returnData(file.getAbsolutePath());
} else if (actions == EditDialogFragment.Actions.NewFolder && !TextUtils.isEmpty(inputText)) {
} else if (actions == EditTextDialog.Actions.NewFolder && !TextUtils.isEmpty(inputText)) {
File file = new File(currentFolder, inputText);
file.mkdirs();
new UpdateList().execute(currentFolder);
@ -340,7 +336,7 @@ public class SelectFileActivity extends Activity implements SearchView.OnQueryTe
""));
} else if (f.isFile()
&& !FilenameUtils.isExtension(f.getName().toLowerCase(), unopenableExtensions)
&& FileUtils.sizeOf(f) <= Constants.MAX_FILE_SIZE * FileUtils.ONE_KB) {
&& FileUtils.sizeOf(f) <= Build.MAX_FILE_SIZE * FileUtils.ONE_KB) {
final long fileSize = f.length();
SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy hh:mm a");
String date = format.format(f.lastModified());

View File

@ -83,8 +83,7 @@ public class AdapterDrawer extends
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.BOLD);
convertView.setBackgroundColor((convertView.getResources()
.getColor(R.color.item_selected)));
}
else {
} else {
hold.nameLabel.setTypeface(hold.nameLabel.getTypeface(), Typeface.NORMAL);
convertView.setBackgroundColor((convertView.getResources()
.getColor(android.R.color.transparent)));
@ -108,8 +107,7 @@ public class AdapterDrawer extends
hold.nameLabel.setTypeface(null, Typeface.BOLD);
convertView.setBackgroundColor((convertView.getResources()
.getColor(R.color.item_selected)));
}
else {
} else {
hold.nameLabel.setTypeface(null, Typeface.NORMAL);
convertView.setBackgroundColor((convertView.getResources()
.getColor(android.R.color.transparent)));

View File

@ -20,18 +20,12 @@
package sharedcode.turboeditor.adapter;
import android.content.Context;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.util.ArrayList;
import sharedcode.turboeditor.R;
public class AdapterTwoItem extends

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.fragment;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.Html;
import android.view.View;
import android.widget.Toast;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.views.DialogHelper;
/**
* Dialog fragment that shows some info about this application.
*
* @author Artem Chepurnoy
*/
public class AboutDialog extends DialogFragment {
private static final String VERSION_UNAVAILABLE = "N/A";
/**
* Merges app name and version name into one.
*/
public static CharSequence getVersionName(Context context) {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
String versionName;
try {
PackageInfo info = pm.getPackageInfo(packageName, 0);
versionName = info.versionName;
// Make the info part of version name a bit smaller.
if (versionName.indexOf('-') >= 0) {
versionName = versionName.replaceFirst("\\-", "<small>-") + "</small>";
}
} catch (PackageManager.NameNotFoundException e) {
versionName = VERSION_UNAVAILABLE;
}
Resources res = context.getResources();
return Html.fromHtml(
res.getString(R.string.about_title,
res.getString(R.string.nome_app_turbo_editor), versionName)
);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Context context = getActivity();
assert context != null;
CharSequence message = Html.fromHtml(getString(
R.string.about_message));
View view = new DialogHelper.Builder(context)
.setIcon(getResources().getDrawable(R.drawable.ic_launcher))
.setTitle(getVersionName(context))
.setMessage(message)
.createCommonView();
return new AlertDialog.Builder(context)
.setView(view)
.setNeutralButton(R.string.close, null)
.create();
}
}

View File

@ -22,30 +22,30 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.view.LayoutInflater;
import it.gmariotti.changelibs.library.view.ChangeLogListView;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.util.Constants;
import sharedcode.turboeditor.util.Build;
public class ChangelogDialogFragment extends DialogFragment {
public class ChangelogDialog extends DialogFragment {
public static void showChangeLogDialog(FragmentManager fragmentManager) {
ChangelogDialogFragment changelogDialogFragment = new ChangelogDialogFragment();
ChangelogDialog changelogDialog = new ChangelogDialog();
FragmentTransaction ft = fragmentManager.beginTransaction();
Fragment prev = fragmentManager.findFragmentByTag("changelogdemo_dialog");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
changelogDialogFragment.show(ft, "changelogdemo_dialog");
changelogDialog.show(ft, "changelogdemo_dialog");
}
@ -72,7 +72,7 @@ public class ChangelogDialogFragment extends DialogFragment {
@Override
public void onClick(final DialogInterface dialog, final int which) {
try {
if (Constants.FOR_AMAZON) {
if (Build.FOR_AMAZON) {
String url = "amzn://apps/android?p=com.maskyn.fileeditor";
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

View File

@ -34,16 +34,16 @@ import android.widget.TextView;
import sharedcode.turboeditor.R;
// ...
public class EditDialogFragment extends DialogFragment implements TextView.OnEditorActionListener {
public class EditTextDialog extends DialogFragment implements TextView.OnEditorActionListener {
private EditText mEditText;
public static EditDialogFragment newInstance(final Actions action) {
return EditDialogFragment.newInstance(action, "");
public static EditTextDialog newInstance(final Actions action) {
return EditTextDialog.newInstance(action, "");
}
public static EditDialogFragment newInstance(final Actions action, final String hint) {
final EditDialogFragment f = new EditDialogFragment();
public static EditTextDialog newInstance(final Actions action, final String hint) {
final EditTextDialog f = new EditTextDialog();
final Bundle args = new Bundle();
args.putSerializable("action", action);
args.putString("hint", hint);

View File

@ -35,7 +35,7 @@ import org.mozilla.universalchardet.Constants;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.preferences.PreferenceHelper;
public class EncodingDialogFragment extends DialogFragment implements AdapterView.OnItemClickListener {
public class EncodingDialog extends DialogFragment implements AdapterView.OnItemClickListener {
private final String[] encodings = new String[]{
Constants.CHARSET_BIG5,
@ -67,8 +67,8 @@ public class EncodingDialogFragment extends DialogFragment implements AdapterVie
};
private ListView list;
public static EncodingDialogFragment newInstance() {
final EncodingDialogFragment f = new EncodingDialogFragment();
public static EncodingDialog newInstance() {
final EncodingDialog f = new EncodingDialog();
return f;
}

View File

@ -25,26 +25,21 @@ import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.NumberPicker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.adapter.AdapterTwoItem;
// ...
public class FileInfoDialogFragment extends DialogFragment {
public class FileInfoDialog extends DialogFragment {
public static FileInfoDialogFragment newInstance(String filePath) {
final FileInfoDialogFragment f = new FileInfoDialogFragment();
public static FileInfoDialog newInstance(String filePath) {
final FileInfoDialog f = new FileInfoDialog();
final Bundle args = new Bundle();
args.putString("filePath", filePath);
f.setArguments(args);

View File

@ -37,16 +37,17 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.util.SearchResult;
import sharedcode.turboeditor.texteditor.SearchResult;
import sharedcode.turboeditor.views.DialogHelper;
// ...
public class FindTextDialogFragment extends DialogFragment {
public class FindTextDialog extends DialogFragment {
private EditText textToFind, textToReplace;
private CheckBox regexCheck, replaceCheck, matchCaseCheck;
public static FindTextDialogFragment newInstance(String allText) {
final FindTextDialogFragment f = new FindTextDialogFragment();
public static FindTextDialog newInstance(String allText) {
final FindTextDialog f = new FindTextDialog();
final Bundle args = new Bundle();
args.putString("allText", allText);
f.setArguments(args);
@ -56,7 +57,13 @@ public class FindTextDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_find_text, null);
//final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_find_text, null);
View view = new DialogHelper.Builder(getActivity())
.setTitle(R.string.find)
.setView(R.layout.dialog_fragment_find_text)
.createSkeletonView();
this.textToFind = (EditText) view.findViewById(R.id.text_to_find);
this.textToReplace = (EditText) view.findViewById(R.id.text_to_replace);
this.regexCheck = (CheckBox) view.findViewById(R.id.regex_check);
@ -79,13 +86,7 @@ public class FindTextDialogFragment extends DialogFragment {
}
}
)
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}
)
.setNegativeButton(android.R.string.cancel, null)
.create();
}
@ -200,7 +201,7 @@ public class FindTextDialogFragment extends DialogFragment {
}
Toast.makeText(getActivity(), String.format(getString(R.string.occurrences_found), foundIndex.size()), Toast.LENGTH_SHORT).show();
// dismiss the dialog
FindTextDialogFragment.this.dismiss();
FindTextDialog.this.dismiss();
}
}
}

View File

@ -37,7 +37,7 @@ import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.util.EventBusEvents;
public class NavigationDrawerListFragment extends Fragment implements AdapterView.OnItemClickListener, AdapterDrawer.Callbacks {
public class NavigationDrawer extends Fragment implements AdapterView.OnItemClickListener, AdapterDrawer.Callbacks {
private AdapterDrawer arrayAdapter;
@ -90,7 +90,6 @@ public class NavigationDrawerListFragment extends Fragment implements AdapterVie
String filePath = savedPaths[position];
// Send the event that a file was selected
EventBus.getDefault().post(new EventBusEvents.NewFileToOpen(new File(filePath)));
EventBus.getDefault().post(new EventBusEvents.AFileIsSelected(filePath));
}
public void onEvent(EventBusEvents.AFileIsSelected event) {
@ -196,9 +195,4 @@ public class NavigationDrawerListFragment extends Fragment implements AdapterVie
if (andCloseOpenedFile)
EventBus.getDefault().post(new EventBusEvents.CannotOpenAFile());
}
/*@Override
public void ItemSelected(String path) {
EventBus.getDefault().post(new EventBusEvents.AFileIsSelected(path));
}*/
}

View File

@ -32,16 +32,17 @@ import java.io.File;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.util.SaveFileTask;
import sharedcode.turboeditor.task.SaveFileTask;
import sharedcode.turboeditor.views.DialogHelper;
// ...
public class NewFileDetailsDialogFragment extends DialogFragment {
public class NewFileDetailsDialog extends DialogFragment {
private EditText mName;
private EditText mFolder;
public static NewFileDetailsDialogFragment newInstance(String fileText, String fileEncoding) {
final NewFileDetailsDialogFragment f = new NewFileDetailsDialogFragment();
public static NewFileDetailsDialog newInstance(String fileText, String fileEncoding) {
final NewFileDetailsDialog f = new NewFileDetailsDialog();
final Bundle args = new Bundle();
args.putString("fileText", fileText);
args.putString("fileEncoding", fileEncoding);
@ -52,7 +53,11 @@ public class NewFileDetailsDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_new_file_details, null);
View view = new DialogHelper.Builder(getActivity())
.setTitle(R.string.file)
.setView(R.layout.dialog_fragment_new_file_details)
.createSkeletonView();
this.mName = (EditText) view.findViewById(android.R.id.text1);
this.mFolder = (EditText) view.findViewById(android.R.id.text2);
@ -71,6 +76,7 @@ public class NewFileDetailsDialogFragment extends DialogFragment {
if (!mName.getText().toString().isEmpty() && !mFolder.getText().toString().isEmpty()) {
File file = new File(mFolder.getText().toString(), mName.getText().toString());
new SaveFileTask(getActivity(), file.getPath(), getArguments().getString("fileText"), getArguments().getString("fileEncoding")).execute();
PreferenceHelper.setWorkingFolder(getActivity(), file.getParent());
}
}
}

View File

@ -1,37 +0,0 @@
/*
* 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.os.Bundle;
import android.app.Fragment;
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);
}
}

View File

@ -24,26 +24,36 @@ import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.util.SaveFileTask;
import sharedcode.turboeditor.task.SaveFileTask;
import sharedcode.turboeditor.views.DialogHelper;
public class SaveFileDialogFragment extends DialogFragment {
public class SaveFileDialog extends DialogFragment {
public static SaveFileDialogFragment newInstance(String filePath, String text, String encoding) {
SaveFileDialogFragment frag = new SaveFileDialogFragment();
public static SaveFileDialog newInstance(String filePath, String text, String encoding) {
return newInstance(filePath, text, encoding, false, "");
}
public static SaveFileDialog newInstance(String filePath, String text, String encoding, boolean openNewFileAfter, String pathOfNewFile) {
SaveFileDialog frag = new SaveFileDialog();
Bundle args = new Bundle();
args.putString("filePath", filePath);
args.putString("text", text);
args.putString("encoding", encoding);
args.putBoolean("openNewFileAfter", openNewFileAfter);
args.putString("pathOfNewFile", pathOfNewFile);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String filePath = getArguments().getString("filePath");
@ -52,33 +62,51 @@ public class SaveFileDialogFragment extends DialogFragment {
final String fileName = FilenameUtils.getName(filePath);
final File file = new File(filePath);
return new AlertDialog.Builder(getActivity())
View view = new DialogHelper.Builder(getActivity())
.setIcon(getResources().getDrawable(R.drawable.ic_action_save))
.setTitle(R.string.salva)
.setMessage(String.format(getString(R.string.save_changes), fileName))
.setPositiveButton(android.R.string.yes,
.createCommonView();
return new AlertDialog.Builder(getActivity())
.setView(view)
.setPositiveButton(R.string.salva,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (!fileName.isEmpty())
new SaveFileTask(getActivity(), filePath, text, encoding).execute();
new SaveFileTask(getActivity(), filePath, text,
encoding).execute();
else {
NewFileDetailsDialogFragment dialogFrag = NewFileDetailsDialogFragment.newInstance(text, encoding);
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
NewFileDetailsDialog dialogFrag =
NewFileDetailsDialog.newInstance(text,
encoding);
dialogFrag.show(getFragmentManager().beginTransaction(),
"dialog");
}
}
}
)
.setNegativeButton(android.R.string.no,
.setNeutralButton(android.R.string.cancel, null)
.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ISaveDialog target = (ISaveDialog) getTargetFragment();
if (target == null) {
target = (ISaveDialog) getActivity();
}
target.userDoesntWantToSave(
getArguments().getBoolean("openNewFileAfter"),
getArguments().getString("pathOfNewFile")
);
}
}
)
.create();
}
public static enum Action {
SaveAFile
public interface ISaveDialog {
void userDoesntWantToSave(boolean openNewFile, String pathOfNewFile);
}
}

View File

@ -28,18 +28,19 @@ import android.view.View;
import android.widget.NumberPicker;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.views.DialogHelper;
// ...
public class SeekbarDialogFragment extends DialogFragment {
public class SeekbarDialog extends DialogFragment {
private NumberPicker mSeekBar;
public static SeekbarDialogFragment newInstance(final Actions action) {
return SeekbarDialogFragment.newInstance(action, 0, 50, 100);
public static SeekbarDialog newInstance(final Actions action) {
return SeekbarDialog.newInstance(action, 0, 50, 100);
}
public static SeekbarDialogFragment newInstance(final Actions action, final int min, final int current, final int max) {
final SeekbarDialogFragment f = new SeekbarDialogFragment();
public static SeekbarDialog newInstance(final Actions action, final int min, final int current, final int max) {
final SeekbarDialog f = new SeekbarDialog();
final Bundle args = new Bundle();
args.putSerializable("action", action);
args.putInt("min", min);
@ -52,9 +53,28 @@ public class SeekbarDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Actions action = (Actions) getArguments().getSerializable("action");
Actions action = (Actions) getArguments().getSerializable("action");
int title;
switch (action){
case FontSize:
title = R.string.font_size;
break;
case SelectPage:
title = R.string.goto_page;
break;
case GoToLine:
title = R.string.goto_line;
break;
default:
title = R.string.nome_app_turbo_editor;
break;
}
View view = new DialogHelper.Builder(getActivity())
.setTitle(title)
.setView(R.layout.dialog_fragment_seekbar)
.createSkeletonView();
final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_seekbar, null);
this.mSeekBar = (NumberPicker) view.findViewById(android.R.id.input);
this.mSeekBar.setMaxValue(getArguments().getInt("max"));
this.mSeekBar.setMinValue(getArguments().getInt("min"));
@ -82,9 +102,9 @@ public class SeekbarDialogFragment extends DialogFragment {
}
void returnData() {
onSeekbarDialogDismissed target = (onSeekbarDialogDismissed) getTargetFragment();
ISeekbarDialog target = (ISeekbarDialog) getTargetFragment();
if (target == null) {
target = (onSeekbarDialogDismissed) getActivity();
target = (ISeekbarDialog) getActivity();
}
target.onSeekbarDialogDismissed(
(Actions) getArguments().getSerializable("action"),
@ -94,10 +114,10 @@ public class SeekbarDialogFragment extends DialogFragment {
}
public enum Actions {
FileSize, SelectPage, GoToLine
FontSize, SelectPage, GoToLine
}
public interface onSeekbarDialogDismissed {
public interface ISeekbarDialog {
void onSeekbarDialogDismissed(Actions action, int value);
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.iab;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* The helper class of donation item.
*
* @author Artem Chepurnoy
*/
public class Donation {
public final int amount;
public final String sku;
public final String text;
public Donation(int amount, String text) {
this.amount = amount;
this.text = text;
// Notice that all of them are defined in
// my Play Store's account!
this.sku = "donation_" + amount;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return new HashCodeBuilder(9, 51)
.append(amount)
.append(text)
.append(sku)
.toHashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (!(o instanceof Donation))
return false;
Donation donation = (Donation) o;
return new EqualsBuilder()
.append(amount, donation.amount)
.append(text, donation.text)
.append(sku, donation.sku)
.isEquals();
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.iab;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.HashSet;
import sharedcode.turboeditor.R;
/**
* Created by achep on 06.05.14 for AcDisplay.
*
* @author Artem Chepurnoy
*/
public class DonationAdapter extends ArrayAdapter<Donation> {
private final HashSet<String> mInventorySet;
private final LayoutInflater mInflater;
private final String mDonationAmountLabel;
private final int mColorNormal;
private final int mColorPurchased;
public DonationAdapter(Context context, Donation[] items, HashSet<String> inventory) {
super(context, 0, items);
mInventorySet = inventory;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Resources res = context.getResources();
mDonationAmountLabel = res.getString(R.string.donation_item_label);
mColorNormal = res.getColor(R.color.donation_normal);
mColorPurchased = res.getColor(R.color.donation_purchased);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Donation donation = getItem(position);
final Holder holder;
final View view;
if (convertView == null) {
holder = new Holder();
view = mInflater.inflate(R.layout.donation_iab_item, parent, false);
assert view != null;
holder.title = (TextView) view.findViewById(android.R.id.title);
holder.summary = (TextView) view.findViewById(android.R.id.summary);
view.setTag(holder);
} else {
view = convertView;
holder = (Holder) view.getTag();
}
boolean bought = mInventorySet.contains(donation.sku);
String amount = Integer.toString(donation.amount);
holder.title.setText(String.format(mDonationAmountLabel, amount));
holder.title.setTextColor(bought ? mColorNormal : mColorPurchased);
holder.summary.setText(donation.text);
holder.summary.setPaintFlags(bought
? holder.summary.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG
: holder.summary.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
return view;
}
private static class Holder {
TextView title;
TextView summary;
}
}

View File

@ -0,0 +1,370 @@
/*
* 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.iab;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.HashSet;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.iab.utils.IabHelper;
import sharedcode.turboeditor.iab.utils.IabResult;
import sharedcode.turboeditor.iab.utils.Inventory;
import sharedcode.turboeditor.iab.utils.Purchase;
import sharedcode.turboeditor.preferences.PreferenceHelper;
import sharedcode.turboeditor.util.Build;
import sharedcode.turboeditor.util.ToastUtils;
import sharedcode.turboeditor.util.ViewUtils;
import sharedcode.turboeditor.views.DialogHelper;
/**
* Fragment that represents an ability to donate to me. Be sure to redirect
* {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
* to this fragment!
*
* @author Artem Chepurnoy
*/
public class DonationFragment extends DialogFragment {
public static final int RC_REQUEST = 10001;
private static final String TAG = "DonationFragment";
private final HashSet<String> mInventorySet = new HashSet<>();
private GridView mGridView;
private ProgressBar mProgressBar;
private TextView mError;
private IabHelper mHelper;
private final IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener =
new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
if (mHelper == null) return;
if (result.isFailure()) {
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
if (!verifyDeveloperPayload(purchase)) {
complain("Error purchasing. Authenticity verification failed.");
setWaitScreen(false);
return;
}
// else, it is a success, the user has donated!
String sku = purchase.getSku();
mInventorySet.add(sku);
PreferenceHelper.setHasDonated(getActivity(), true);
}
};
private Donation[] mDonationList;
private final IabHelper.QueryInventoryFinishedListener mGotInventoryListener =
new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (mHelper == null) return;
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
mInventorySet.clear();
for (Donation donation : mDonationList) {
Purchase purchase = inventory.getPurchase(donation.sku);
boolean isBought = (purchase != null && verifyDeveloperPayload(purchase));
if (isBought) {
mInventorySet.add(donation.sku);
PreferenceHelper.setHasDonated(getActivity(), true);
}
}
/*
// Fake items to debug user interface.
mInventorySet.add(mDonationList[0].sku);
mInventorySet.add(mDonationList[1].sku);
mInventorySet.add(mDonationList[2].sku);
*/
updateUi();
setWaitScreen(false);
}
};
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mDonationList = DonationItems.get(getResources());
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Activity activity = getActivity();
assert activity != null;
View view = new DialogHelper.Builder(activity)
.setTitle(R.string.donation_title)
.setView(R.layout.donation_dialog)
.createSkeletonView();
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
.setView(view)
.setNegativeButton(android.R.string.cancel, null);
TextView info = (TextView) view.findViewById(R.id.info);
info.setText(Html.fromHtml(getString(R.string.donation_info)));
info.setMovementMethod(new LinkMovementMethod());
mError = (TextView) view.findViewById(R.id.error);
mProgressBar = (ProgressBar) view.findViewById(android.R.id.progress);
mGridView = (GridView) view.findViewById(R.id.grid);
mGridView.setAdapter(new DonationAdapter(getActivity(), mDonationList, mInventorySet));
mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
DonationAdapter adapter = (DonationAdapter) parent.getAdapter();
Donation donation = adapter.getItem(position);
if (!mInventorySet.contains(donation.sku)) {
/**
* See {@link sharedcode.turboeditor.iab.DonationFragment#verifyDeveloperPayload(Purchase)}.
*/
String payload = "";
try {
mHelper.launchPurchaseFlow(
getActivity(), donation.sku, RC_REQUEST,
mPurchaseFinishedListener, payload);
} catch (Exception e) {
ToastUtils.showShort(getActivity(), "Failed to launch a purchase flow.");
}
} else {
ToastUtils.showShort(getActivity(), getString(R.string.donation_item_bought));
}
}
});
final AlertDialog alertDialog;
// Show PayPal button.
final Intent paypalIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(Build.Links.DONATE));
builder.setNeutralButton(R.string.paypal, null);
alertDialog = builder.create();
alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
Data[] datas = new Data[]{
new Data(
alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL),
paypalIntent, R.drawable.ic_action_paypal)
};
ImageSpan span;
SpannableString text;
for (final Data data : datas) {
final Button btn = data.button;
if (btn != null) {
span = new ImageSpan(getActivity(), data.iconResource);
// Replace text with an icon.
// This is a workaround to fix compound button's aligment.
text = new SpannableString(" ");
text.setSpan(span, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
btn.setText(text);
// Eat default weight.
btn.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startPaymentIntentWithWarningAlertDialog(data.intent);
}
});
}
}
}
final class Data {
private final Button button;
private final Intent intent;
private final int iconResource;
private Data(Button button, Intent intent, int iconResource) {
this.button = button;
this.intent = intent;
this.iconResource = iconResource;
}
}
});
initBilling();
return alertDialog;
}
/**
* Shows a warning alert dialog to note, that those methods
* may suck hard and nobody will care about it.<br/>
* Starts an intent if user is agree with it.
*/
private void startPaymentIntentWithWarningAlertDialog(final Intent intent) {
CharSequence messageText = getString(R.string.donation_no_responsibility);
new DialogHelper.Builder(getActivity())
.setMessage(messageText)
.wrap()
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
startActivity(intent);
dismiss(); // Dismiss main fragment
} catch (ActivityNotFoundException e) { /* hell no */ }
}
})
.create()
.show();
}
private void setWaitScreen(boolean loading) {
ViewUtils.setVisible(mProgressBar, loading);
ViewUtils.setVisible(mGridView, !loading);
ViewUtils.setVisible(mError, false);
}
private void setErrorScreen(String errorMessage, final Runnable runnable) {
mProgressBar.setVisibility(View.GONE);
mGridView.setVisibility(View.GONE);
mError.setVisibility(View.VISIBLE);
mError.setText(errorMessage);
mError.setOnClickListener(runnable != null ? new View.OnClickListener() {
@Override
public void onClick(View v) {
runnable.run();
}
} : null);
}
/**
* Updates GUI to display changes.
*/
private void updateUi() {
DonationAdapter adapter = (DonationAdapter) mGridView.getAdapter();
adapter.notifyDataSetChanged();
}
@Override
public void onDestroy() {
super.onDestroy();
disposeBilling();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mHelper.handleActivityResult(requestCode, resultCode, data)) {
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* Releases billing service.
*
* @see #initBilling()
*/
private void disposeBilling() {
if (mHelper != null) {
mHelper.dispose();
mHelper = null;
}
}
/**
* <b>Make sure you call {@link #disposeBilling()}!</b>
*
* @see #disposeBilling()
*/
private void initBilling() {
setWaitScreen(true);
disposeBilling();
String base64EncodedPublicKey = Build.GOOGLE_PLAY_PUBLIC_KEY;
mHelper = new IabHelper(getActivity(), base64EncodedPublicKey);
mHelper.enableDebugLogging(Build.DEBUG);
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (mHelper == null) return;
if (!result.isSuccess()) {
setErrorScreen(getString(R.string.donation_error_iab_setup), new Runnable() {
@Override
public void run() {
// Try to initialize billings again.
initBilling();
}
});
return;
}
setWaitScreen(false);
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
private boolean verifyDeveloperPayload(Purchase purchase) {
// TODO: This method itself is a big question.
// Personally, I think that this whole best practices part
// is confusing and is trying to make you do work that the API
// should really be doing. Since the purchase is tied to a Google account,
// and the Play Store obviously saves this information, they should
// just give you this in the purchase details. Getting a proper user ID
// requires additional permissions that you shouldnt need to add just
// to cover for the deficiencies of the IAB API.
return true;
}
private void complain(String message) {
ToastUtils.showShort(getActivity(), message);
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.iab;
import android.content.res.Resources;
import sharedcode.turboeditor.R;
/**
* Created by achep on 07.05.14 for AcDisplay.
*
* @author Artem Chepurnoy
*/
public class DonationItems {
public static Donation[] get(Resources res) {
int[] data = new int[]{
2, R.string.donation_2,
4, R.string.donation_4,
10, R.string.donation_10,
20, R.string.donation_20,
50, R.string.donation_50,
99, R.string.donation_99,
};
Donation[] donation = new Donation[data.length / 2];
int length = donation.length;
for (int i = 0; i < length; i++) {
donation[i] = new Donation(data[i * 2],
res.getString(data[i * 2 + 1]));
}
return donation;
}
}

View File

@ -0,0 +1,582 @@
// Portions copyright 2002, Google, Inc.
//
// 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 sharedcode.turboeditor.iab.utils;
// This code was converted from code at http://iharder.sourceforge.net/base64/
// Lots of extraneous features were removed.
/* The original code said:
* <p>
* I am placing this code in the Public Domain. Do with it as you will.
* This software comes with no guarantees or warranties but with
* plenty of well-wishing instead!
* Please visit
* <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
* periodically to check for updates or to contribute improvements.
* </p>
*
* @author Robert Harder
* @author rharder@usa.net
* @version 1.3
*/
/**
* Base64 converter class. This code is not a complete MIME encoder;
* it simply converts binary data to base64 data and back.
* <p/>
* <p>Note {@link CharBase64} is a GWT-compatible implementation of this
* class.
*/
public class Base64 {
/**
* Specify encoding (value is {@code true}).
*/
public final static boolean ENCODE = true;
/**
* Specify decoding (value is {@code false}).
*/
public final static boolean DECODE = false;
/**
* The equals sign (=) as a byte.
*/
private final static byte EQUALS_SIGN = (byte) '=';
/**
* The new line character (\n) as a byte.
*/
private final static byte NEW_LINE = (byte) '\n';
/**
* The 64 valid Base64 values.
*/
private final static byte[] ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '+', (byte) '/'};
/**
* The 64 valid web safe Base64 values.
*/
private final static byte[] WEBSAFE_ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '-', (byte) '_'};
/**
* Translates a Base64 value to either its 6-bit reconstruction value
* or a negative number indicating some other meaning.
*/
private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9, -9, -9, // Decimal 44 - 46
63, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
/**
* The web safe decodabet
*/
private final static byte[] WEBSAFE_DECODABET =
{-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
62, // Dash '-' sign at decimal 45
-9, -9, // Decimal 46-47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, // Decimal 91-94
63, // Underscore '_' at decimal 95
-9, // Decimal 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
// Indicates white space in encoding
private final static byte WHITE_SPACE_ENC = -5;
// Indicates equals sign in encoding
private final static byte EQUALS_SIGN_ENC = -1;
/**
* Defeats instantiation.
*/
private Base64() {
}
/* ******** E N C O D I N G M E T H O D S ******** */
/**
* Encodes up to three bytes of the array <var>source</var>
* and writes the resulting four Base64 bytes to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 3 for
* the <var>source</var> array or <var>destOffset</var> + 4 for
* the <var>destination</var> array.
* The actual number of significant bytes in your array is
* given by <var>numSigBytes</var>.
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param alphabet is the encoding alphabet
* @return the <var>destination</var> array
* @since 1.3
*/
private static byte[] encode3to4(byte[] source, int srcOffset,
int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index alphabet
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff =
(numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
} // end switch
} // end encode3to4
/**
* Encodes a byte array into Base64 notation.
* Equivalent to calling
* {@code encodeBytes(source, 0, source.length)}
*
* @param source The data to convert
* @since 1.4
*/
public static String encode(byte[] source) {
return encode(source, 0, source.length, ALPHABET, true);
}
/**
* Encodes a byte array into web safe Base64 notation.
*
* @param source The data to convert
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
*/
public static String encodeWebSafe(byte[] source, boolean doPadding) {
return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet the encoding alphabet
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
* @since 1.4
*/
public static String encode(byte[] source, int off, int len, byte[] alphabet,
boolean doPadding) {
byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
int outLen = outBuff.length;
// If doPadding is false, set length to truncate '='
// padding characters
while (doPadding == false && outLen > 0) {
if (outBuff[outLen - 1] != '=') {
break;
}
outLen -= 1;
}
return new String(outBuff, 0, outLen);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet is the encoding alphabet
* @param maxLineLength maximum length of one line.
* @return the BASE64-encoded byte array
*/
public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
int maxLineLength) {
int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
int len43 = lenDiv3 * 4;
byte[] outBuff = new byte[len43 // Main 4:3
+ (len43 / maxLineLength)]; // New lines
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; d += 3, e += 4) {
// The following block of code is the same as
// encode3to4( source, d + off, 3, outBuff, e, alphabet );
// but inlined for faster encoding (~20% improvement)
int inBuff =
((source[d + off] << 24) >>> 8)
| ((source[d + 1 + off] << 24) >>> 16)
| ((source[d + 2 + off] << 24) >>> 24);
outBuff[e] = alphabet[(inBuff >>> 18)];
outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
lineLength += 4;
if (lineLength == maxLineLength) {
outBuff[e + 4] = NEW_LINE;
e++;
lineLength = 0;
} // end if: end of line
} // end for: each piece of array
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e, alphabet);
lineLength += 4;
if (lineLength == maxLineLength) {
// Add a last newline
outBuff[e + 4] = NEW_LINE;
e++;
}
e += 4;
}
assert (e == outBuff.length);
return outBuff;
}
/* ******** D E C O D I N G M E T H O D S ******** */
/**
* Decodes four bytes from array <var>source</var>
* and writes the resulting bytes (up to three of them)
* to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 4 for
* the <var>source</var> array or <var>destOffset</var> + 3 for
* the <var>destination</var> array.
* This method returns the actual number of bytes that
* were converted from the Base64 encoding.
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param decodabet the decodabet for decoding Base64 content
* @return the number of decoded bytes converted
* @since 1.3
*/
private static int decode4to3(byte[] source, int srcOffset,
byte[] destination, int destOffset, byte[] decodabet) {
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
} else if (source[srcOffset + 3] == EQUALS_SIGN) {
// Example: DkL=
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
} else {
// Example: DkLE
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
| ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
}
} // end decodeToBytes
/**
* Decodes data from Base64 notation.
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
* @since 1.4
*/
public static byte[] decode(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decode(bytes, 0, bytes.length);
}
/**
* Decodes data from web safe Base64 notation.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decodeWebSafe(bytes, 0, bytes.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source The Base64 encoded data
* @return decoded data
* @throws Base64DecoderException
* @since 1.3
*/
public static byte[] decode(byte[] source) throws Base64DecoderException {
return decode(source, 0, source.length);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded data.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(byte[] source)
throws Base64DecoderException {
return decodeWebSafe(source, 0, source.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
* @throws Base64DecoderException
* @since 1.3
*/
public static byte[] decode(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, DECODABET);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded byte array.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
*/
public static byte[] decodeWebSafe(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, WEBSAFE_DECODABET);
}
/**
* Decodes Base64 content using the supplied decodabet and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @param decodabet the decodabet for decoding Base64 content
* @return decoded data
*/
public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
throws Base64DecoderException {
int len34 = len * 3 / 4;
byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiCrop = 0;
byte sbiDecode = 0;
for (i = 0; i < len; i++) {
sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
sbiDecode = decodabet[sbiCrop];
if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
if (sbiDecode >= EQUALS_SIGN_ENC) {
// An equals sign (for padding) must not occur at position 0 or 1
// and must be the last byte[s] in the encoded value
if (sbiCrop == EQUALS_SIGN) {
int bytesLeft = len - i;
byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
if (b4Posn == 0 || b4Posn == 1) {
throw new Base64DecoderException(
"invalid padding byte '=' at byte offset " + i);
} else if ((b4Posn == 3 && bytesLeft > 2)
|| (b4Posn == 4 && bytesLeft > 1)) {
throw new Base64DecoderException(
"padding byte '=' falsely signals end of encoded value "
+ "at offset " + i
);
} else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
throw new Base64DecoderException(
"encoded value has invalid trailing byte");
}
break;
}
b4[b4Posn++] = sbiCrop;
if (b4Posn == 4) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
b4Posn = 0;
}
}
} else {
throw new Base64DecoderException("Bad Base64 input character at " + i
+ ": " + source[i + off] + "(decimal)");
}
}
// Because web safe encoding allows non padding base64 encodes, we
// need to pad the rest of the b4 buffer with equal signs when
// b4Posn != 0. There can be at most 2 equal signs at the end of
// four characters, so the b4 buffer must have two or three
// characters. This also catches the case where the input is
// padded with EQUALS_SIGN
if (b4Posn != 0) {
if (b4Posn == 1) {
throw new Base64DecoderException("single trailing character at offset "
+ (len - 1));
}
b4[b4Posn++] = EQUALS_SIGN;
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
}
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
}

View File

@ -17,12 +17,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.iab.utils;
import android.os.Build;
/**
* Exception thrown when encountering an invalid Base64 input character.
*
* @author nelson
*/
public class Base64DecoderException extends Exception {
private static final long serialVersionUID = 1L;
public class ApiHelper {
public static boolean is11(){
return Build.VERSION.SDK_INT >= 11;
public Base64DecoderException() {
super();
}
public Base64DecoderException(String s) {
super(s);
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.iab.utils;
/**
* Exception thrown when something went wrong with in-app billing.
* An IabException has an associated IabResult (an error).
* To get the IAB result that caused this exception to be thrown,
* call {@link #getResult()}.
*/
public class IabException extends Exception {
IabResult mResult;
public IabException(IabResult r) {
this(r, null);
}
public IabException(int response, String message) {
this(new IabResult(response, message));
}
public IabException(IabResult r, Exception cause) {
super(r.getMessage(), cause);
mResult = r;
}
public IabException(int response, String message, Exception cause) {
this(new IabResult(response, message), cause);
}
/**
* Returns the IAB result (error) that this exception signals.
*/
public IabResult getResult() {
return mResult;
}
}

View File

@ -0,0 +1,966 @@
/*
* 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.iab.utils;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.content.ServiceConnection;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONException;
import com.android.vending.billing.*;
import java.util.ArrayList;
import java.util.List;
/**
* Provides convenience methods for in-app billing. You can create one instance of this
* class for your application and use it to process in-app billing operations.
* It provides synchronous (blocking) and asynchronous (non-blocking) methods for
* many common in-app billing operations, as well as automatic signature
* verification.
* <p/>
* After instantiating, you must perform setup in order to start using the object.
* To perform setup, call the {@link #startSetup} method and provide a listener;
* that listener will be notified when setup is complete, after which (and not before)
* you may call other methods.
* <p/>
* After setup is complete, you will typically want to request an inventory of owned
* items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync}
* and related methods.
* <p/>
* When you are done with this object, don't forget to call {@link #dispose}
* to ensure proper cleanup. This object holds a binding to the in-app billing
* service, which will leak unless you dispose of it correctly. If you created
* the object on an Activity's onCreate method, then the recommended
* place to dispose of it is the Activity's onDestroy method.
* <p/>
* A note about threading: When using this object from a background thread, you may
* call the blocking versions of methods; when using from a UI thread, call
* only the asynchronous versions and handle the results via callbacks.
* Also, notice that you can only call one asynchronous operation at a time;
* attempting to start a second asynchronous operation while the first one
* has not yet completed will result in an exception being thrown.
*
* @author Bruno Oliveira (Google)
*/
public class IabHelper {
// Billing response codes
public static final int BILLING_RESPONSE_RESULT_OK = 0;
public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
public static final int BILLING_RESPONSE_RESULT_ERROR = 6;
public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;
// IAB Helper error codes
public static final int IABHELPER_ERROR_BASE = -1000;
public static final int IABHELPER_REMOTE_EXCEPTION = -1001;
public static final int IABHELPER_BAD_RESPONSE = -1002;
public static final int IABHELPER_VERIFICATION_FAILED = -1003;
public static final int IABHELPER_SEND_INTENT_FAILED = -1004;
public static final int IABHELPER_USER_CANCELLED = -1005;
public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006;
public static final int IABHELPER_MISSING_TOKEN = -1007;
public static final int IABHELPER_UNKNOWN_ERROR = -1008;
public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009;
public static final int IABHELPER_INVALID_CONSUMPTION = -1010;
// Keys for the responses from InAppBillingService
public static final String RESPONSE_CODE = "RESPONSE_CODE";
public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST";
public static final String RESPONSE_BUY_INTENT = "BUY_INTENT";
public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE";
public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST";
public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST";
public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST";
public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN";
// Item types
public static final String ITEM_TYPE_INAPP = "inapp";
public static final String ITEM_TYPE_SUBS = "subs";
// some fields on the getSkuDetails response bundle
public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST";
public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST";
// Is debug logging enabled?
boolean mDebugLog = false;
String mDebugTag = "IabHelper";
// Is setup done?
boolean mSetupDone = false;
// Has this object been disposed of? (If so, we should ignore callbacks, etc)
boolean mDisposed = false;
boolean mIsBound = false;
// Are subscriptions supported?
boolean mSubscriptionsSupported = false;
// Is an asynchronous operation in progress?
// (only one at a time can be in progress)
boolean mAsyncInProgress = false;
// (for logging/debugging)
// if mAsyncInProgress == true, what asynchronous operation is in progress?
String mAsyncOperation = "";
// Context we were passed during initialization
Context mContext;
// Connection to the service
IInAppBillingService mService;
ServiceConnection mServiceConn;
// The request code used to launch purchase flow
int mRequestCode;
// The item type of the current purchase flow
String mPurchasingItemType;
// Public key for verifying signature, in base64 encoding
String mSignatureBase64 = null;
// The listener registered on launchPurchaseFlow, which we have to call back when
// the purchase finishes
OnIabPurchaseFinishedListener mPurchaseListener;
/**
* Creates an instance. After creation, it will not yet be ready to use. You must perform
* setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not
* block and is safe to call from a UI thread.
*
* @param ctx Your application or Activity context. Needed to bind to the in-app billing service.
* @param base64PublicKey Your application's public key, encoded in base64.
* This is used for verification of purchase signatures. You can find your app's base64-encoded
* public key in your application's page on Google Play Developer Console. Note that this
* is NOT your "developer public key".
*/
public IabHelper(Context ctx, String base64PublicKey) {
mContext = ctx.getApplicationContext();
mSignatureBase64 = base64PublicKey;
logDebug("IAB helper created.");
}
/**
* Returns a human-readable description for the given response code.
*
* @param code The response code
* @return A human-readable string explaining the result code.
* It also includes the result code numerically.
*/
public static String getResponseDesc(int code) {
String[] iab_msgs = ("0:OK/1:User Canceled/2:Unknown/" +
"3:Billing Unavailable/4:Item unavailable/" +
"5:Developer Error/6:Error/7:Item Already Owned/" +
"8:Item not owned").split("/");
String[] iabhelper_msgs = ("0:OK/-1001:Remote exception during initialization/" +
"-1002:Bad response received/" +
"-1003:Purchase signature verification failed/" +
"-1004:Send intent failed/" +
"-1005:User cancelled/" +
"-1006:Unknown purchase response/" +
"-1007:Missing token/" +
"-1008:Unknown error/" +
"-1009:Subscriptions not available/" +
"-1010:Invalid consumption attempt").split("/");
if (code <= IABHELPER_ERROR_BASE) {
int index = IABHELPER_ERROR_BASE - code;
if (index >= 0 && index < iabhelper_msgs.length) return iabhelper_msgs[index];
else return String.valueOf(code) + ":Unknown IAB Helper Error";
} else if (code < 0 || code >= iab_msgs.length)
return String.valueOf(code) + ":Unknown";
else
return iab_msgs[code];
}
/**
* Enables or disable debug logging through LogCat.
*/
public void enableDebugLogging(boolean enable, String tag) {
checkNotDisposed();
mDebugLog = enable;
mDebugTag = tag;
}
public void enableDebugLogging(boolean enable) {
checkNotDisposed();
mDebugLog = enable;
}
/**
* Starts the setup process. This will start up the setup process asynchronously.
* You will be notified through the listener when the setup process is complete.
* This method is safe to call from a UI thread.
*
* @param listener The listener to notify when the setup process is complete.
*/
public void startSetup(final OnIabSetupFinishedListener listener) {
// If already set up, can't do it again.
checkNotDisposed();
if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");
// Connection to IAB service
logDebug("Starting in-app billing setup.");
mServiceConn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
logDebug("Billing service disconnected.");
mService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (mDisposed) return;
logDebug("Billing service connected.");
mService = IInAppBillingService.Stub.asInterface(service);
String packageName = mContext.getPackageName();
try {
logDebug("Checking for in-app billing 3 support.");
// check for in-app billing v3 support
int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);
if (response != BILLING_RESPONSE_RESULT_OK) {
if (listener != null) listener.onIabSetupFinished(new IabResult(response,
"Error checking for billing v3 support."));
// if in-app purchases aren't supported, neither are subscriptions.
mSubscriptionsSupported = false;
return;
}
logDebug("In-app billing version 3 supported for " + packageName);
// check for v3 subscriptions support
response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);
if (response == BILLING_RESPONSE_RESULT_OK) {
logDebug("Subscriptions AVAILABLE.");
mSubscriptionsSupported = true;
} else {
logDebug("Subscriptions NOT AVAILABLE. Response: " + response);
}
mSetupDone = true;
} catch (RemoteException e) {
if (listener != null) {
listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
"RemoteException while setting up in-app billing."));
}
e.printStackTrace();
return;
}
if (listener != null) {
listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
}
}
};
Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
serviceIntent.setPackage("com.android.vending");
List<ResolveInfo> ri = mContext.getPackageManager().queryIntentServices(serviceIntent, 0);
if (ri != null && !ri.isEmpty()) {
// service available to handle that Intent
mIsBound = mContext.bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
} else {
// no service available to handle that Intent
if (listener != null) {
listener.onIabSetupFinished(
new IabResult(BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE,
"Billing service unavailable on device.")
);
}
}
}
/**
* Dispose of object, releasing resources. It's very important to call this
* method when you are done with this object. It will release any resources
* used by it such as service connections. Naturally, once the object is
* disposed of, it can't be used again.
*/
public void dispose() {
logDebug("Disposing.");
mSetupDone = false;
if (mServiceConn != null) {
logDebug("Unbinding from service.");
if (mContext != null && mIsBound) {
mContext.unbindService(mServiceConn);
}
}
mDisposed = true;
mContext = null;
mServiceConn = null;
mService = null;
mPurchaseListener = null;
}
private void checkNotDisposed() {
if (mDisposed)
throw new IllegalStateException("IabHelper was disposed of, so it cannot be used.");
}
/**
* Returns whether subscriptions are supported.
*/
public boolean subscriptionsSupported() {
checkNotDisposed();
return mSubscriptionsSupported;
}
public void launchPurchaseFlow(Activity act, String sku, int requestCode, OnIabPurchaseFinishedListener listener) {
launchPurchaseFlow(act, sku, requestCode, listener, "");
}
public void launchPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
launchPurchaseFlow(act, sku, ITEM_TYPE_INAPP, requestCode, listener, extraData);
}
public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener) {
launchSubscriptionPurchaseFlow(act, sku, requestCode, listener, "");
}
public void launchSubscriptionPurchaseFlow(Activity act, String sku, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
launchPurchaseFlow(act, sku, ITEM_TYPE_SUBS, requestCode, listener, extraData);
}
/**
* Initiate the UI flow for an in-app purchase. Call this method to initiate an in-app purchase,
* which will involve bringing up the Google Play screen. The calling activity will be paused while
* the user interacts with Google Play, and the result will be delivered via the activity's
* {@link android.app.Activity#onActivityResult} method, at which point you must call
* this object's {@link #handleActivityResult} method to continue the purchase flow. This method
* MUST be called from the UI thread of the Activity.
*
* @param act The calling activity.
* @param sku The sku of the item to purchase.
* @param itemType indicates if it's a product or a subscription (ITEM_TYPE_INAPP or ITEM_TYPE_SUBS)
* @param requestCode A request code (to differentiate from other responses --
* as in {@link android.app.Activity#startActivityForResult}).
* @param listener The listener to notify when the purchase process finishes
* @param extraData Extra data (developer payload), which will be returned with the purchase data
* when the purchase completes. This extra data will be permanently bound to that purchase
* and will always be returned when the purchase is queried.
*/
public void launchPurchaseFlow(Activity act, String sku, String itemType, int requestCode,
OnIabPurchaseFinishedListener listener, String extraData) {
checkNotDisposed();
checkSetupDone("launchPurchaseFlow");
flagStartAsync("launchPurchaseFlow");
IabResult result;
if (itemType.equals(ITEM_TYPE_SUBS) && !mSubscriptionsSupported) {
IabResult r = new IabResult(IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE,
"Subscriptions are not available.");
flagEndAsync();
if (listener != null) listener.onIabPurchaseFinished(r, null);
return;
}
try {
logDebug("Constructing buy intent for " + sku + ", item type: " + itemType);
Bundle buyIntentBundle = mService.getBuyIntent(3, mContext.getPackageName(), sku, itemType, extraData);
int response = getResponseCodeFromBundle(buyIntentBundle);
if (response != BILLING_RESPONSE_RESULT_OK) {
logError("Unable to buy item, Error response: " + getResponseDesc(response));
flagEndAsync();
result = new IabResult(response, "Unable to buy item");
if (listener != null) listener.onIabPurchaseFinished(result, null);
return;
}
PendingIntent pendingIntent = buyIntentBundle.getParcelable(RESPONSE_BUY_INTENT);
logDebug("Launching buy intent for " + sku + ". Request code: " + requestCode);
mRequestCode = requestCode;
mPurchaseListener = listener;
mPurchasingItemType = itemType;
act.startIntentSenderForResult(pendingIntent.getIntentSender(),
requestCode, new Intent(),
0, 0,
0);
} catch (SendIntentException e) {
logError("SendIntentException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_SEND_INTENT_FAILED, "Failed to send intent.");
if (listener != null) listener.onIabPurchaseFinished(result, null);
} catch (RemoteException e) {
logError("RemoteException while launching purchase flow for sku " + sku);
e.printStackTrace();
flagEndAsync();
result = new IabResult(IABHELPER_REMOTE_EXCEPTION, "Remote exception while starting purchase flow");
if (listener != null) listener.onIabPurchaseFinished(result, null);
}
}
/**
* Handles an activity result that's part of the purchase flow in in-app billing. If you
* are calling {@link #launchPurchaseFlow}, then you must call this method from your
* Activity's {@link android.app.Activity@onActivityResult} method. This method
* MUST be called from the UI thread of the Activity.
*
* @param requestCode The requestCode as you received it.
* @param resultCode The resultCode as you received it.
* @param data The data (Intent) as you received it.
* @return Returns true if the result was related to a purchase flow and was handled;
* false if the result was not related to a purchase, in which case you should
* handle it normally.
*/
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
IabResult result;
if (requestCode != mRequestCode) return false;
checkNotDisposed();
checkSetupDone("handleActivityResult");
// end of async purchase operation that started on launchPurchaseFlow
flagEndAsync();
if (data == null) {
logError("Null data in IAB activity result.");
result = new IabResult(IABHELPER_BAD_RESPONSE, "Null data in IAB result");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
int responseCode = getResponseCodeFromIntent(data);
String purchaseData = data.getStringExtra(RESPONSE_INAPP_PURCHASE_DATA);
String dataSignature = data.getStringExtra(RESPONSE_INAPP_SIGNATURE);
if (resultCode == Activity.RESULT_OK && responseCode == BILLING_RESPONSE_RESULT_OK) {
logDebug("Successful resultcode from purchase activity.");
logDebug("Purchase data: " + purchaseData);
logDebug("Data signature: " + dataSignature);
logDebug("Extras: " + data.getExtras());
logDebug("Expected item type: " + mPurchasingItemType);
if (purchaseData == null || dataSignature == null) {
logError("BUG: either purchaseData or dataSignature is null.");
logDebug("Extras: " + data.getExtras().toString());
result = new IabResult(IABHELPER_UNKNOWN_ERROR, "IAB returned null purchaseData or dataSignature");
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
@SuppressWarnings("UnusedAssignment") Purchase purchase = null;
try {
purchase = new Purchase(mPurchasingItemType, purchaseData, dataSignature);
String sku = purchase.getSku();
// Verify signature
if (!Security.verifyPurchase(mSignatureBase64, purchaseData, dataSignature)) {
logError("Purchase signature verification FAILED for sku " + sku);
result = new IabResult(IABHELPER_VERIFICATION_FAILED, "Signature verification failed for sku " + sku);
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, purchase);
return true;
}
logDebug("Purchase signature successfully verified.");
} catch (JSONException e) {
logError("Failed to parse purchase data.");
e.printStackTrace();
result = new IabResult(IABHELPER_BAD_RESPONSE, "Failed to parse purchase data.");
if (mPurchaseListener != null)
mPurchaseListener.onIabPurchaseFinished(result, null);
return true;
}
if (mPurchaseListener != null) {
mPurchaseListener.onIabPurchaseFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Success"), purchase);
}
} else if (resultCode == Activity.RESULT_OK) {
// result code was OK, but in-app billing response was not OK.
logDebug("Result code was OK but in-app billing response was not OK: " + getResponseDesc(responseCode));
if (mPurchaseListener != null) {
result = new IabResult(responseCode, "Problem purchashing item.");
mPurchaseListener.onIabPurchaseFinished(result, null);
}
} else if (resultCode == Activity.RESULT_CANCELED) {
logDebug("Purchase canceled - Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_USER_CANCELLED, "User canceled.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
} else {
logError("Purchase failed. Result code: " + Integer.toString(resultCode)
+ ". Response: " + getResponseDesc(responseCode));
result = new IabResult(IABHELPER_UNKNOWN_PURCHASE_RESPONSE, "Unknown purchase response.");
if (mPurchaseListener != null) mPurchaseListener.onIabPurchaseFinished(result, null);
}
return true;
}
public Inventory queryInventory(boolean querySkuDetails, List<String> moreSkus) throws IabException {
return queryInventory(querySkuDetails, moreSkus, null);
}
/**
* Queries the inventory. This will query all owned items from the server, as well as
* information on additional skus, if specified. This method may block or take long to execute.
* Do not call from a UI thread. For that, use the non-blocking version {@link #refreshInventoryAsync}.
*
* @param querySkuDetails if true, SKU details (price, description, etc) will be queried as well
* as purchase information.
* @param moreItemSkus additional PRODUCT skus to query information on, regardless of ownership.
* Ignored if null or if querySkuDetails is false.
* @param moreSubsSkus additional SUBSCRIPTIONS skus to query information on, regardless of ownership.
* Ignored if null or if querySkuDetails is false.
* @throws IabException if a problem occurs while refreshing the inventory.
*/
public Inventory queryInventory(boolean querySkuDetails, List<String> moreItemSkus,
List<String> moreSubsSkus) throws IabException {
checkNotDisposed();
checkSetupDone("queryInventory");
try {
Inventory inv = new Inventory();
int r = queryPurchases(inv, ITEM_TYPE_INAPP);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying owned items).");
}
if (querySkuDetails) {
r = querySkuDetails(ITEM_TYPE_INAPP, inv, moreItemSkus);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying prices of items).");
}
}
// if subscriptions are supported, then also query for subscriptions
if (mSubscriptionsSupported) {
r = queryPurchases(inv, ITEM_TYPE_SUBS);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying owned subscriptions).");
}
if (querySkuDetails) {
r = querySkuDetails(ITEM_TYPE_SUBS, inv, moreItemSkus);
if (r != BILLING_RESPONSE_RESULT_OK) {
throw new IabException(r, "Error refreshing inventory (querying prices of subscriptions).");
}
}
}
return inv;
} catch (RemoteException e) {
throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while refreshing inventory.", e);
} catch (JSONException e) {
throw new IabException(IABHELPER_BAD_RESPONSE, "Error parsing JSON response while refreshing inventory.", e);
}
}
/**
* Asynchronous wrapper for inventory query. This will perform an inventory
* query as described in {@link #queryInventory}, but will do so asynchronously
* and call back the specified listener upon completion. This method is safe to
* call from a UI thread.
*
* @param querySkuDetails as in {@link #queryInventory}
* @param moreSkus as in {@link #queryInventory}
* @param listener The listener to notify when the refresh operation completes.
*/
public void queryInventoryAsync(final boolean querySkuDetails,
final List<String> moreSkus,
final QueryInventoryFinishedListener listener) {
final Handler handler = new Handler();
checkNotDisposed();
checkSetupDone("queryInventory");
flagStartAsync("refresh inventory");
(new Thread(new Runnable() {
public void run() {
IabResult result = new IabResult(BILLING_RESPONSE_RESULT_OK, "Inventory refresh successful.");
Inventory inv = null;
try {
inv = queryInventory(querySkuDetails, moreSkus);
} catch (IabException ex) {
result = ex.getResult();
}
flagEndAsync();
final IabResult result_f = result;
final Inventory inv_f = inv;
if (!mDisposed && listener != null) {
handler.post(new Runnable() {
public void run() {
listener.onQueryInventoryFinished(result_f, inv_f);
}
});
}
}
})).start();
}
public void queryInventoryAsync(QueryInventoryFinishedListener listener) {
queryInventoryAsync(true, null, listener);
}
public void queryInventoryAsync(boolean querySkuDetails, QueryInventoryFinishedListener listener) {
queryInventoryAsync(querySkuDetails, null, listener);
}
/**
* Consumes a given in-app product. Consuming can only be done on an item
* that's owned, and as a result of consumption, the user will no longer own it.
* This method may block or take long to return. Do not call from the UI thread.
* For that, see {@link #consumeAsync}.
*
* @param itemInfo The PurchaseInfo that represents the item to consume.
* @throws IabException if there is a problem during consumption.
*/
void consume(Purchase itemInfo) throws IabException {
checkNotDisposed();
checkSetupDone("consume");
if (!itemInfo.mItemType.equals(ITEM_TYPE_INAPP)) {
throw new IabException(IABHELPER_INVALID_CONSUMPTION,
"Items of type '" + itemInfo.mItemType + "' can't be consumed.");
}
try {
String token = itemInfo.getToken();
String sku = itemInfo.getSku();
if (token == null || token.equals("")) {
logError("Can't consume " + sku + ". No token.");
throw new IabException(IABHELPER_MISSING_TOKEN, "PurchaseInfo is missing token for sku: "
+ sku + " " + itemInfo);
}
logDebug("Consuming sku: " + sku + ", token: " + token);
int response = mService.consumePurchase(3, mContext.getPackageName(), token);
if (response == BILLING_RESPONSE_RESULT_OK) {
logDebug("Successfully consumed sku: " + sku);
} else {
logDebug("Error consuming consuming sku " + sku + ". " + getResponseDesc(response));
throw new IabException(response, "Error consuming sku " + sku);
}
} catch (RemoteException e) {
throw new IabException(IABHELPER_REMOTE_EXCEPTION, "Remote exception while consuming. PurchaseInfo: " + itemInfo, e);
}
}
/**
* Asynchronous wrapper to item consumption. Works like {@link #consume}, but
* performs the consumption in the background and notifies completion through
* the provided listener. This method is safe to call from a UI thread.
*
* @param purchase The purchase to be consumed.
* @param listener The listener to notify when the consumption operation finishes.
*/
public void consumeAsync(Purchase purchase, OnConsumeFinishedListener listener) {
checkNotDisposed();
checkSetupDone("consume");
List<Purchase> purchases = new ArrayList<>();
purchases.add(purchase);
consumeAsyncInternal(purchases, listener, null);
}
/**
* Same as {@link consumeAsync}, but for multiple items at once.
*
* @param purchases The list of PurchaseInfo objects representing the purchases to consume.
* @param listener The listener to notify when the consumption operation finishes.
*/
public void consumeAsync(List<Purchase> purchases, OnConsumeMultiFinishedListener listener) {
checkNotDisposed();
checkSetupDone("consume");
consumeAsyncInternal(purchases, null, listener);
}
// Checks that setup was done; if not, throws an exception.
void checkSetupDone(String operation) {
if (!mSetupDone) {
logError("Illegal state for operation (" + operation + "): IAB helper is not set up.");
throw new IllegalStateException("IAB helper is not set up. Can't perform operation: " + operation);
}
}
// Workaround to bug where sometimes response codes come as Long instead of Integer
int getResponseCodeFromBundle(Bundle b) {
Object o = b.get(RESPONSE_CODE);
if (o == null) {
logDebug("Bundle with null response code, assuming OK (known issue)");
return BILLING_RESPONSE_RESULT_OK;
} else if (o instanceof Integer) return (Integer) o;
else if (o instanceof Long) return (int) ((Long) o).longValue();
else {
logError("Unexpected type for bundle response code.");
logError(o.getClass().getName());
throw new RuntimeException("Unexpected type for bundle response code: " + o.getClass().getName());
}
}
// Workaround to bug where sometimes response codes come as Long instead of Integer
int getResponseCodeFromIntent(Intent i) {
Object o = i.getExtras().get(RESPONSE_CODE);
if (o == null) {
logError("Intent with no response code, assuming OK (known issue)");
return BILLING_RESPONSE_RESULT_OK;
} else if (o instanceof Integer) return (Integer) o;
else if (o instanceof Long) return (int) ((Long) o).longValue();
else {
logError("Unexpected type for intent response code.");
logError(o.getClass().getName());
throw new RuntimeException("Unexpected type for intent response code: " + o.getClass().getName());
}
}
void flagStartAsync(String operation) {
if (mAsyncInProgress) throw new IllegalStateException("Can't start async operation (" +
operation + ") because another async operation(" + mAsyncOperation + ") is in progress.");
mAsyncOperation = operation;
mAsyncInProgress = true;
logDebug("Starting async operation: " + operation);
}
void flagEndAsync() {
logDebug("Ending async operation: " + mAsyncOperation);
mAsyncOperation = "";
mAsyncInProgress = false;
}
int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException {
// Query purchases
logDebug("Querying owned items, item type: " + itemType);
logDebug("Package name: " + mContext.getPackageName());
boolean verificationFailed = false;
String continueToken = null;
do {
logDebug("Calling getPurchases with continuation token: " + continueToken);
Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(),
itemType, continueToken);
int response = getResponseCodeFromBundle(ownedItems);
logDebug("Owned items response: " + String.valueOf(response));
if (response != BILLING_RESPONSE_RESULT_OK) {
logDebug("getPurchases() failed: " + getResponseDesc(response));
return response;
}
if (!ownedItems.containsKey(RESPONSE_INAPP_ITEM_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_PURCHASE_DATA_LIST)
|| !ownedItems.containsKey(RESPONSE_INAPP_SIGNATURE_LIST)) {
logError("Bundle returned from getPurchases() doesn't contain required fields.");
return IABHELPER_BAD_RESPONSE;
}
ArrayList<String> ownedSkus = ownedItems.getStringArrayList(
RESPONSE_INAPP_ITEM_LIST);
ArrayList<String> purchaseDataList = ownedItems.getStringArrayList(
RESPONSE_INAPP_PURCHASE_DATA_LIST);
ArrayList<String> signatureList = ownedItems.getStringArrayList(
RESPONSE_INAPP_SIGNATURE_LIST);
for (int i = 0; i < purchaseDataList.size(); ++i) {
String purchaseData = purchaseDataList.get(i);
String signature = signatureList.get(i);
String sku = ownedSkus.get(i);
if (Security.verifyPurchase(mSignatureBase64, purchaseData, signature)) {
logDebug("Sku is owned: " + sku);
Purchase purchase = new Purchase(itemType, purchaseData, signature);
if (TextUtils.isEmpty(purchase.getToken())) {
logWarn("BUG: empty/null token!");
logDebug("Purchase data: " + purchaseData);
}
// Record ownership and token
inv.addPurchase(purchase);
} else {
logWarn("Purchase signature verification **FAILED**. Not adding item.");
logDebug(" Purchase data: " + purchaseData);
logDebug(" Signature: " + signature);
verificationFailed = true;
}
}
continueToken = ownedItems.getString(INAPP_CONTINUATION_TOKEN);
logDebug("Continuation token: " + continueToken);
} while (!TextUtils.isEmpty(continueToken));
return verificationFailed ? IABHELPER_VERIFICATION_FAILED : BILLING_RESPONSE_RESULT_OK;
}
int querySkuDetails(String itemType, Inventory inv, List<String> moreSkus)
throws RemoteException, JSONException {
logDebug("Querying SKU details.");
ArrayList<String> skuList = new ArrayList<>();
skuList.addAll(inv.getAllOwnedSkus(itemType));
if (moreSkus != null) {
for (String sku : moreSkus) {
if (!skuList.contains(sku)) {
skuList.add(sku);
}
}
}
if (skuList.size() == 0) {
logDebug("queryPrices: nothing to do because there are no SKUs.");
return BILLING_RESPONSE_RESULT_OK;
}
Bundle querySkus = new Bundle();
querySkus.putStringArrayList(GET_SKU_DETAILS_ITEM_LIST, skuList);
Bundle skuDetails = mService.getSkuDetails(3, mContext.getPackageName(),
itemType, querySkus);
if (!skuDetails.containsKey(RESPONSE_GET_SKU_DETAILS_LIST)) {
int response = getResponseCodeFromBundle(skuDetails);
if (response != BILLING_RESPONSE_RESULT_OK) {
logDebug("getSkuDetails() failed: " + getResponseDesc(response));
return response;
} else {
logError("getSkuDetails() returned a bundle with neither an error nor a detail list.");
return IABHELPER_BAD_RESPONSE;
}
}
ArrayList<String> responseList = skuDetails.getStringArrayList(
RESPONSE_GET_SKU_DETAILS_LIST);
for (String thisResponse : responseList) {
SkuDetails d = new SkuDetails(itemType, thisResponse);
logDebug("Got sku details: " + d);
inv.addSkuDetails(d);
}
return BILLING_RESPONSE_RESULT_OK;
}
void consumeAsyncInternal(final List<Purchase> purchases,
final OnConsumeFinishedListener singleListener,
final OnConsumeMultiFinishedListener multiListener) {
final Handler handler = new Handler();
flagStartAsync("consume");
(new Thread(new Runnable() {
public void run() {
final List<IabResult> results = new ArrayList<>();
for (Purchase purchase : purchases) {
try {
consume(purchase);
results.add(new IabResult(BILLING_RESPONSE_RESULT_OK, "Successful consume of sku " + purchase.getSku()));
} catch (IabException ex) {
results.add(ex.getResult());
}
}
flagEndAsync();
if (!mDisposed && singleListener != null) {
handler.post(new Runnable() {
public void run() {
singleListener.onConsumeFinished(purchases.get(0), results.get(0));
}
});
}
if (!mDisposed && multiListener != null) {
handler.post(new Runnable() {
public void run() {
multiListener.onConsumeMultiFinished(purchases, results);
}
});
}
}
})).start();
}
void logDebug(String msg) {
if (mDebugLog) Log.d(mDebugTag, msg);
}
void logError(String msg) {
Log.e(mDebugTag, "In-app billing error: " + msg);
}
void logWarn(String msg) {
Log.w(mDebugTag, "In-app billing warning: " + msg);
}
/**
* Callback for setup process. This listener's {@link #onIabSetupFinished} method is called
* when the setup process is complete.
*/
public interface OnIabSetupFinishedListener {
/**
* Called to notify that setup is complete.
*
* @param result The result of the setup process.
*/
public void onIabSetupFinished(IabResult result);
}
/**
* Callback that notifies when a purchase is finished.
*/
public interface OnIabPurchaseFinishedListener {
/**
* Called to notify that an in-app purchase finished. If the purchase was successful,
* then the sku parameter specifies which item was purchased. If the purchase failed,
* the sku and extraData parameters may or may not be null, depending on how far the purchase
* process went.
*
* @param result The result of the purchase.
* @param info The purchase information (null if purchase failed)
*/
public void onIabPurchaseFinished(IabResult result, Purchase info);
}
/**
* Listener that notifies when an inventory query operation completes.
*/
public interface QueryInventoryFinishedListener {
/**
* Called to notify that an inventory query operation completed.
*
* @param result The result of the operation.
* @param inv The inventory.
*/
public void onQueryInventoryFinished(IabResult result, Inventory inv);
}
/**
* Callback that notifies when a consumption operation finishes.
*/
public interface OnConsumeFinishedListener {
/**
* Called to notify that a consumption has finished.
*
* @param purchase The purchase that was (or was to be) consumed.
* @param result The result of the consumption operation.
*/
public void onConsumeFinished(Purchase purchase, IabResult result);
}
/**
* Callback that notifies when a multi-item consumption operation finishes.
*/
public interface OnConsumeMultiFinishedListener {
/**
* Called to notify that a consumption of multiple items has finished.
*
* @param purchases The purchases that were (or were to be) consumed.
* @param results The results of each consumption operation, corresponding to each
* sku.
*/
public void onConsumeMultiFinished(List<Purchase> purchases, List<IabResult> results);
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.iab.utils;
/**
* Represents the result of an in-app billing operation.
* A result is composed of a response code (an integer) and possibly a
* message (String). You can get those by calling
* {@link #getResponse} and {@link #getMessage()}, respectively. You
* can also inquire whether a result is a success or a failure by
* calling {@link #isSuccess()} and {@link #isFailure()}.
*/
public class IabResult {
int mResponse;
String mMessage;
public IabResult(int response, String message) {
mResponse = response;
if (message == null || message.trim().length() == 0) {
mMessage = IabHelper.getResponseDesc(response);
} else {
mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")";
}
}
public int getResponse() {
return mResponse;
}
public String getMessage() {
return mMessage;
}
public boolean isSuccess() {
return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK;
}
public boolean isFailure() {
return !isSuccess();
}
public String toString() {
return "IabResult: " + getMessage();
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.iab.utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a block of information about in-app items.
* An Inventory is returned by such methods as {@link IabHelper#queryInventory}.
*/
public class Inventory {
Map<String, SkuDetails> mSkuMap = new HashMap<>();
Map<String, Purchase> mPurchaseMap = new HashMap<>();
Inventory() {
}
/**
* Returns the listing details for an in-app product.
*/
public SkuDetails getSkuDetails(String sku) {
return mSkuMap.get(sku);
}
/**
* Returns purchase information for a given product, or null if there is no purchase.
*/
public Purchase getPurchase(String sku) {
return mPurchaseMap.get(sku);
}
/**
* Returns whether or not there exists a purchase of the given product.
*/
public boolean hasPurchase(String sku) {
return mPurchaseMap.containsKey(sku);
}
/**
* Return whether or not details about the given product are available.
*/
public boolean hasDetails(String sku) {
return mSkuMap.containsKey(sku);
}
/**
* Erase a purchase (locally) from the inventory, given its product ID. This just
* modifies the Inventory object locally and has no effect on the server! This is
* useful when you have an existing Inventory object which you know to be up to date,
* and you have just consumed an item successfully, which means that erasing its
* purchase data from the Inventory you already have is quicker than querying for
* a new Inventory.
*/
public void erasePurchase(String sku) {
if (mPurchaseMap.containsKey(sku)) mPurchaseMap.remove(sku);
}
/**
* Returns a list of all owned product IDs.
*/
List<String> getAllOwnedSkus() {
return new ArrayList<>(mPurchaseMap.keySet());
}
/**
* Returns a list of all owned product IDs of a given type
*/
List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) result.add(p.getSku());
}
return result;
}
/**
* Returns a list of all purchases.
*/
List<Purchase> getAllPurchases() {
return new ArrayList<>(mPurchaseMap.values());
}
void addSkuDetails(SkuDetails d) {
mSkuMap.put(d.getSku(), d);
}
void addPurchase(Purchase p) {
mPurchaseMap.put(p.getSku(), p);
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.iab.utils;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app billing purchase.
*/
public class Purchase {
String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS
String mOrderId;
String mPackageName;
String mSku;
long mPurchaseTime;
int mPurchaseState;
String mDeveloperPayload;
String mToken;
String mOriginalJson;
String mSignature;
public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException {
mItemType = itemType;
mOriginalJson = jsonPurchaseInfo;
JSONObject o = new JSONObject(mOriginalJson);
mOrderId = o.optString("orderId");
mPackageName = o.optString("packageName");
mSku = o.optString("productId");
mPurchaseTime = o.optLong("purchaseTime");
mPurchaseState = o.optInt("purchaseState");
mDeveloperPayload = o.optString("developerPayload");
mToken = o.optString("token", o.optString("purchaseToken"));
mSignature = signature;
}
public String getItemType() {
return mItemType;
}
public String getOrderId() {
return mOrderId;
}
public String getPackageName() {
return mPackageName;
}
public String getSku() {
return mSku;
}
public long getPurchaseTime() {
return mPurchaseTime;
}
public int getPurchaseState() {
return mPurchaseState;
}
public String getDeveloperPayload() {
return mDeveloperPayload;
}
public String getToken() {
return mToken;
}
public String getOriginalJson() {
return mOriginalJson;
}
public String getSignature() {
return mSignature;
}
@Override
public String toString() {
return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson;
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.iab.utils;
import android.text.TextUtils;
import android.util.Log;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
/**
* Security-related methods. For a secure implementation, all of this code
* should be implemented on a server that communicates with the
* application on the device. For the sake of simplicity and clarity of this
* example, this code is included here and is executed on the device. If you
* must verify the purchases on the phone, you should obfuscate this code to
* make it harder for an attacker to replace the code with stubs that treat all
* purchases as verified.
*/
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* Verifies that the data was signed with the given signature, and returns
* the verified purchase. The data is in JSON format and signed
* with a private key. The data also contains the {@link PurchaseState}
* and product ID of the purchase.
*
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
}
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
throw new IllegalArgumentException(e);
}
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
Signature sig;
try {
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
}
return false;
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.iab.utils;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app product's listing details.
*/
public class SkuDetails {
String mItemType;
String mSku;
String mType;
String mPrice;
String mTitle;
String mDescription;
String mJson;
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
public String getSku() {
return mSku;
}
public String getType() {
return mType;
}
public String getPrice() {
return mPrice;
}
public String getTitle() {
return mTitle;
}
public String getDescription() {
return mDescription;
}
@Override
public String toString() {
return "SkuDetails:" + mJson;
}
}

View File

@ -35,6 +35,7 @@ import java.util.List;
import de.greenrobot.event.EventBus;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.util.EventBusEvents;
import sharedcode.turboeditor.util.ThemeUtils;
/**
* A {@link PreferenceActivity} that presents a set of application settings. On
@ -107,12 +108,7 @@ public class ExtraSettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
boolean light = PreferenceHelper.getLightTheme(this);
if (light) {
setTheme(R.style.AppTheme_Light);
} else {
setTheme(R.style.AppTheme_Dark);
}
ThemeUtils.setTheme(this);
super.onCreate(savedInstanceState);
}

View File

@ -109,6 +109,9 @@ public final class PreferenceHelper {
return getPrefs(context).getBoolean("page_system_active", true);
}
public static boolean hasDonated(Context context) {
return getPrefs(context).getBoolean("has_donated", false);
}
// Setter methods
public static void setUseMonospace(Context context, boolean value) {
@ -151,4 +154,8 @@ public final class PreferenceHelper {
getEditor(context).putBoolean("read_only", value).commit();
}
public static void setHasDonated(Context context, boolean value) {
getEditor(context).putBoolean("has_donated", value);
}
}

View File

@ -21,7 +21,6 @@ package sharedcode.turboeditor.preferences;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -32,8 +31,11 @@ import android.widget.TextView;
import de.greenrobot.event.EventBus;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.fragment.SeekbarDialogFragment;
import sharedcode.turboeditor.fragment.SeekbarDialog;
import sharedcode.turboeditor.util.AnimationUtils;
import sharedcode.turboeditor.util.ProCheckUtils;
import sharedcode.turboeditor.util.ViewUtils;
import sharedcode.turboeditor.views.DialogHelper;
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged;
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE;
@ -43,7 +45,7 @@ import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChan
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX;
import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT;
public class SettingsFragment extends Fragment implements SeekbarDialogFragment.onSeekbarDialogDismissed {
public class SettingsFragment extends Fragment implements SeekbarDialog.ISeekbarDialog {
// Editor Variables
private boolean sLineNumbers;
@ -67,7 +69,7 @@ public class SettingsFragment extends Fragment implements SeekbarDialogFragment.
Bundle savedInstanceState) {
// Our custom layout
View rootView = inflater.inflate(R.layout.fragment_settings, container, false);
final CheckBox switchLineNumbers, switchSyntax, switchWrapContent, switchMonospace, switchLightTheme, switchSuggestionsActive, switchAutoSave, switchReadOnly, switchSendErrorReports;
final CheckBox switchLineNumbers, switchSyntax, switchWrapContent, switchMonospace, switchReadOnly;
switchLineNumbers = (CheckBox) rootView.findViewById(R.id.switch_line_numbers);
switchSyntax = (CheckBox) rootView.findViewById(R.id.switch_syntax);
switchWrapContent = (CheckBox) rootView.findViewById(R.id.switch_wrap_content);
@ -80,10 +82,13 @@ public class SettingsFragment extends Fragment implements SeekbarDialogFragment.
switchMonospace.setChecked(sUseMonospace);
switchReadOnly.setChecked(sReadOnly);
TextView fontSizeView, goProView, extraOptionsView;
TextView fontSizeView, donateView, extraOptionsView;
fontSizeView = (TextView) rootView.findViewById(R.id.drawer_button_font_size);
extraOptionsView = (TextView) rootView.findViewById(R.id.drawer_button_extra_options);
goProView = (TextView) rootView.findViewById(R.id.drawer_button_go_pro);
donateView = (TextView) rootView.findViewById(R.id.drawer_button_go_pro);
if(ProCheckUtils.isPro(getActivity(), false))
ViewUtils.setVisible(donateView, false);
switchLineNumbers.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
@ -139,7 +144,8 @@ public class SettingsFragment extends Fragment implements SeekbarDialogFragment.
int fontCurrent = //(int) (mEditor.getTextSize() / scaledDensity);
//fontMax / 2;
PreferenceHelper.getFontSize(getActivity());
SeekbarDialogFragment dialogFrag = SeekbarDialogFragment.newInstance(SeekbarDialogFragment.Actions.FileSize, 1, fontCurrent, fontMax);
SeekbarDialog dialogFrag = SeekbarDialog.newInstance(SeekbarDialog.Actions
.FontSize, 1, fontCurrent, fontMax);
dialogFrag.setTargetFragment(SettingsFragment.this, 0);
dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");
}
@ -148,27 +154,23 @@ public class SettingsFragment extends Fragment implements SeekbarDialogFragment.
extraOptionsView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), ExtraSettingsActivity.class));
AnimationUtils.startActivityWithScale(getActivity(), new Intent(getActivity(),
ExtraSettingsActivity.class), false, 0, v);
}
});
goProView.setOnClickListener(new View.OnClickListener() {
donateView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditorpro"))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} catch (Exception e) {
}
DialogHelper.showDonateDialog(getActivity());
}
});
goProView.setVisibility(ProCheckUtils.isPro(getActivity()) ? View.GONE : View.VISIBLE);
return rootView;
}
@Override
public void onSeekbarDialogDismissed(SeekbarDialogFragment.Actions action, int value) {
public void onSeekbarDialogDismissed(SeekbarDialog.Actions action, int value) {
PreferenceHelper.setFontSize(getActivity(), value);
EventBus.getDefault().post(new APreferenceValueWasChanged(FONT_SIZE));

View File

@ -17,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.root;
import android.util.Log;
import java.io.BufferedReader;
import java.io.DataOutputStream;
@ -26,8 +28,6 @@ import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.util.Log;
public class LinuxShell {
private static final String UNIX_ESCAPE_EXPRESSION = "(\\(|\\)|\\[|\\]|\\s|\'|\"|`|\\{|\\}|&|\\\\|\\?)";

View File

@ -32,7 +32,7 @@
* along with 920 Text Editor. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.root;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.task;
import android.content.Context;
import android.os.AsyncTask;
@ -32,6 +32,8 @@ import java.util.concurrent.TimeoutException;
import de.greenrobot.event.EventBus;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.root.RootUtils;
import sharedcode.turboeditor.util.EventBusEvents;
public class SaveFileTask extends AsyncTask<Void, Void, Void> {

View File

@ -17,11 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import android.content.Context;
public class EdittextPadding {
import sharedcode.turboeditor.util.PixelDipConverter;
public class EditTextPadding {
public static int getPaddingWithoutLineNumbers(Context context) {
return (int) PixelDipConverter.convertDpToPixel(5, context);

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import org.mozilla.universalchardet.UniversalDetector;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import android.text.Layout;
import android.widget.ScrollView;
@ -85,6 +85,7 @@ public class LineUtils {
/**
* Gets the line from the index of the letter in the text
*
* @param index
* @param lineCount
* @param layout

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import android.content.Context;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import android.content.Context;
import android.os.Handler;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import java.util.regex.Pattern;

View File

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
package sharedcode.turboeditor.texteditor;
import java.util.LinkedList;

View File

@ -19,12 +19,7 @@
package sharedcode.turboeditor.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
@ -36,6 +31,10 @@ import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class AccessStorageApi {
public static Bitmap loadPrescaledBitmap(String filename) throws IOException {

View File

@ -0,0 +1,52 @@
/*
* 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.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityOptionsCompat;
import android.view.View;
public class AnimationUtils {
public static Bundle getScaleBundle(View view) {
return ActivityOptionsCompat.makeScaleUpAnimation(
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
}
public static void startActivityWithScale(@NonNull Activity startActivity, @NonNull Intent subActivity, @NonNull boolean forResult, @Nullable int code, @NonNull View view) {
if(forResult){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
startActivity.startActivityForResult(subActivity, code, AnimationUtils.getScaleBundle
(view));
else
startActivity.startActivityForResult(subActivity, code);
}
else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
startActivity.startActivity(subActivity, AnimationUtils.getScaleBundle
(view));
else
startActivity.startActivity(subActivity);
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
import sharedcode.turboeditor.BuildConfig;
/**
* Created by Artem on 30.12.13.
*/
public final class Build {
public static final boolean DEBUG = BuildConfig.DEBUG;
public static final String SUPPORT_EMAIL = "maskyngames@gmail.com";
public static final String GOOGLE_PLAY_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvpZca3gZSeRTHPMgxM+A1nTXuRL+9NTOWA1VJFs6ytppcO96i9EWQhmtXVUOKRQIgbXvkepxF7ut+JEjrbniQubZvmyBs9DxK7xUN4Zc3ZboQDQdfg2HJmZXzn8+joOfjXdS9WzsW7aaWKIQ8QXgOB1RUm9hdMdAlgKw+cEyp27WOoMK5m2H/i7C0MIO9tEQs3Hn9UTjayzzfy3MY+KDaX3T6oKievegbpyqyt8y4cpVusJC+uQFLa4bHKPtA3MaPUG6kU9tRV/DHrvFV6dOaPuTYCnYJELlGNfeqRUF0Nvb3Sv0U+BUoXgevjrlLdLz1bqgPDibLzaQmmofNXOnVQIDAQAB";
public static final int MAX_FILE_SIZE = 20_000;
public static final boolean FOR_AMAZON = false;
public static class Links {
public static final String GITHUB = "http://github.com/vmihalachi/TurboEditor";
public static final String XDA = "http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016";
public static final String TRANSLATE = "http://crowdin.net/project/turbo-client";
public static final String DONATE = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=26VWS2TSAMUJA";
public static final String GOOGLE_PLUS_COMMUNITY = "http://plus.google.com/u/0/communities/111974095419108178946";
public static final String AMAZON_STORE = "amzn://apps/android?p=com.maskyn.fileeditor";
public static final String PLAY_STORE = "market://search?q=pub:Maskyn";
}
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
public class Constants {
public static final int MAX_FILE_SIZE = 20_000;
public static final boolean FOR_AMAZON = false;
}

View File

@ -0,0 +1,71 @@
/*
* 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.os.Build;
/**
* Contains params of current device. This is nice because we can override
* some here to test compatibility with old API.
*
* @author Artem Chepurnoy
*/
public class Device {
/**
* @return {@code true} if device is device supports given API version,
* {@code false} otherwise.
*/
public static boolean hasTargetApi(int api) {
return Build.VERSION.SDK_INT >= api;
}
/**
* @return {@code true} if device is running
* {@link android.os.Build.VERSION_CODES#L Lemon Cake} or higher, {@code false} otherwise.
*/
public static boolean hasLemonCakeApi() {
return Build.VERSION.SDK_INT >= 20; // Build.VERSION_CODES.L;
}
/**
* @return {@code true} if device is running
* {@link android.os.Build.VERSION_CODES#KITKAT KitKat} or higher, {@code false} otherwise.
*/
public static boolean hasKitKatApi() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
/**
* @return {@code true} if device is running
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2 Jelly Bean 4.3} or higher, {@code false} otherwise.
*/
public static boolean hasJellyBeanMR2Api() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
}
/**
* @return {@code true} if device is running
* {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1 Jelly Bean 4.2} or higher, {@code false} otherwise.
*/
public static boolean hasJellyBeanMR1Api() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
}

View File

@ -1,32 +0,0 @@
/*
* 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 sharedcode.turboeditor.views.GoodScrollView;
public interface EditorInterface {
public GoodScrollView getVerticalScrollView();
public String getFilePath();
public PageSystem getPageSystem();
public void updateTextSyntax();
}

View File

@ -21,16 +21,24 @@ package sharedcode.turboeditor.util;
import android.content.Context;
import sharedcode.turboeditor.preferences.PreferenceHelper;
public class ProCheckUtils {
public static boolean isPro(Context context) {
public static boolean isPro(Context context, boolean includeDonations) {
String packageName = context.getPackageName();
if (Constants.FOR_AMAZON)
if (Build.FOR_AMAZON)
return true;
else if (packageName.equals("com.maskyn.fileeditorpro"))
return true;
else if (includeDonations && PreferenceHelper.hasDonated(context))
return true;
else
return false;
}
public static boolean isPro(Context context) {
return isPro(context, true);
}
}

View File

@ -24,7 +24,16 @@ import android.app.Activity;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.preferences.PreferenceHelper;
public class ThemeHelper {
public class ThemeUtils {
public static void setTheme(Activity activity){
boolean light = PreferenceHelper.getLightTheme(activity);
if (light) {
activity.setTheme(R.style.AppThemeBaseLight);
} else {
activity.setTheme(R.style.AppThemeBaseDark);
}
}
public static void setWindowsBackground(Activity activity) {
boolean whiteTheme = PreferenceHelper.getLightTheme(activity);

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2014 Vlad Mihalachi
*
* This file is part of Turbo Editor.
*
* Turbo Editor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Turbo Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package sharedcode.turboeditor.util;
import android.content.Context;
import android.support.annotation.NonNull;
import android.widget.Toast;
/**
* Helper class with utils related to toasts (no bacon.)
*
* @author Artem Chepurnoy
*/
public class ToastUtils {
/**
* Shows toast message with given message shortly.
*
* @param text message to show
* @see #showLong(android.content.Context, CharSequence)
*/
@NonNull
public static Toast showShort(@NonNull Context context, @NonNull CharSequence text) {
return show(context, text, Toast.LENGTH_SHORT);
}
@NonNull
public static Toast showShort(@NonNull Context context, int stringRes) {
return showShort(context, context.getString(stringRes));
}
/**
* Shows toast message with given message for a long time.
*
* @param text message to show
* @see #showShort(android.content.Context, CharSequence)
*/
@NonNull
public static Toast showLong(@NonNull Context context, @NonNull CharSequence text) {
return show(context, text, Toast.LENGTH_LONG);
}
@NonNull
public static Toast showLong(@NonNull Context context, int stringRes) {
return showLong(context, context.getString(stringRes));
}
@NonNull
private static Toast show(@NonNull Context context, CharSequence text, int duration) {
Toast toast = Toast.makeText(context, text, duration);
toast.show();
return toast;
}
}

View File

@ -0,0 +1,305 @@
/*
* 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.graphics.Matrix;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Artem on 21.01.14.
*/
public class ViewUtils {
private static final String TAG = "ViewUtils";
@NonNull
private static final MotionEventHandler MOTION_EVENT_HANDLER = Device.hasKitKatApi()
? new MotionEventHandlerReflection()
: new MotionEventHandlerCompat();
public static boolean isTouchPointInView(@NonNull View view, float x, float y) {
final int[] coordinates = new int[3];
view.getLocationInWindow(coordinates);
int left = coordinates[0];
int top = coordinates[1];
return x >= left && x <= left + view.getWidth() &&
y >= top && y <= top + view.getHeight();
}
public static int getLeft(@NonNull View view) {
final int[] coordinates = new int[3];
view.getLocationInWindow(coordinates);
return coordinates[0];
}
public static int getTop(@NonNull View view) {
final int[] coordinates = new int[3];
view.getLocationInWindow(coordinates);
return coordinates[1];
}
public static int getBottom(@NonNull View view) {
return getTop(view) + view.getHeight();
}
public static int indexOf(@NonNull ViewGroup container, @NonNull View view) {
int length = container.getChildCount();
for (int i = 0; i < length; i++) {
View child = container.getChildAt(i);
assert child != null;
if (child.equals(view)) {
return i;
}
}
return -1;
}
// //////////////////////////////////////////
// //////////// -- VISIBILITY -- ////////////
// //////////////////////////////////////////
public static void setVisible(@NonNull View view, boolean visible) {
setVisible(view, visible, View.GONE);
}
public static void setVisible(@NonNull View view, boolean visible, int invisibleFlag) {
int visibility = view.getVisibility();
int visibilityNew = visible ? View.VISIBLE : invisibleFlag;
if (visibility != visibilityNew) {
view.setVisibility(visibilityNew);
}
}
public static void safelySetText(@NonNull TextView textView, @Nullable CharSequence text) {
final boolean visible = text != null;
if (visible) textView.setText(text);
ViewUtils.setVisible(textView, visible);
}
// //////////////////////////////////////////
// /////////// -- TOUCH EVENTS -- ///////////
// //////////////////////////////////////////
public static boolean pointInView(@NonNull View view, float localX, float localY, float slop) {
return localX >= view.getLeft() - slop
&& localX < view.getRight() + slop
&& localY >= view.getTop() - slop
&& localY < view.getBottom() + slop;
}
/**
* Transforms a motion event from view-local coordinates to on-screen
* coordinates.
*
* @param ev the view-local motion event
* @return false if the transformation could not be applied
*/
public static boolean toGlobalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
return MOTION_EVENT_HANDLER.toGlobalMotionEvent(view, ev);
}
/**
* Transforms a motion event from on-screen coordinates to view-local
* coordinates.
*
* @param ev the on-screen motion event
* @return false if the transformation could not be applied
*/
public static boolean toLocalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
return MOTION_EVENT_HANDLER.toLocalMotionEvent(view, ev);
}
private static abstract class MotionEventHandler {
/**
* Transforms a motion event from view-local coordinates to on-screen
* coordinates.
*
* @param ev the view-local motion event
* @return false if the transformation could not be applied
*/
abstract boolean toGlobalMotionEvent(@NonNull View view, @NonNull MotionEvent ev);
/**
* Transforms a motion event from on-screen coordinates to view-local
* coordinates.
*
* @param ev the on-screen motion event
* @return false if the transformation could not be applied
*/
abstract boolean toLocalMotionEvent(@NonNull View view, @NonNull MotionEvent ev);
}
private static final class MotionEventHandlerReflection extends MotionEventHandler {
@Override
boolean toGlobalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
return toMotionEvent(view, ev, "toGlobalMotionEvent");
}
@Override
boolean toLocalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
return toMotionEvent(view, ev, "toLocalMotionEvent");
}
private boolean toMotionEvent(View view, MotionEvent ev, String methodName) {
try {
Method method = View.class.getDeclaredMethod(methodName, MotionEvent.class);
method.setAccessible(true);
return (boolean) method.invoke(view, ev);
} catch (InvocationTargetException
| IllegalAccessException
| NoSuchMethodException
| NoClassDefFoundError e) {
Log.wtf(TAG, "Failed to access motion event transforming!!!");
e.printStackTrace();
}
return false;
}
}
private static final class MotionEventHandlerCompat extends MotionEventHandler {
@Nullable
private static int[] getWindowPosition(@NonNull View view) {
Object info;
try {
Field field = View.class.getDeclaredField("mAttachInfo");
field.setAccessible(true);
info = field.get(view);
} catch (Exception e) {
info = null;
Log.e(TAG, "Failed to get AttachInfo.");
}
if (info == null) {
return null;
}
int[] position = new int[2];
try {
Class clazz = Class.forName("android.view.View$AttachInfo");
Field field = clazz.getDeclaredField("mWindowLeft");
field.setAccessible(true);
position[0] = field.getInt(info);
field = clazz.getDeclaredField("mWindowTop");
field.setAccessible(true);
position[1] = field.getInt(info);
} catch (Exception e) {
Log.e(TAG, "Failed to get window\'s position from AttachInfo.");
return null;
}
return position;
}
/**
* Recursive helper method that applies transformations in post-order.
*
* @param ev the on-screen motion event
*/
private static void transformMotionEventToLocal(@NonNull View view, @NonNull MotionEvent ev) {
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View vp = (View) parent;
transformMotionEventToLocal(vp, ev);
ev.offsetLocation(vp.getScrollX(), vp.getScrollY());
} // TODO: Use reflections to access ViewRootImpl
// else if (parent instanceof ViewRootImpl) {
// final ViewRootImpl vr = (ViewRootImpl) parent;
// ev.offsetLocation(0, vr.mCurScrollY);
// }
ev.offsetLocation(-view.getLeft(), -view.getTop());
Matrix matrix = view.getMatrix();
if (matrix != null) {
ev.transform(matrix);
}
}
/**
* Recursive helper method that applies transformations in pre-order.
*
* @param ev the on-screen motion event
*/
private static void transformMotionEventToGlobal(@NonNull View view, @NonNull MotionEvent ev) {
Matrix matrix = view.getMatrix();
if (matrix != null) {
ev.transform(matrix);
}
ev.offsetLocation(view.getLeft(), view.getTop());
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View vp = (View) parent;
ev.offsetLocation(-vp.getScrollX(), -vp.getScrollY());
transformMotionEventToGlobal(vp, ev);
} // TODO: Use reflections to access ViewRootImpl
// else if (parent instanceof ViewRootImpl) {
// final ViewRootImpl vr = (ViewRootImpl) parent;
// ev.offsetLocation(0, -vr.mCurScrollY);
// }
}
@Override
boolean toGlobalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
final int[] windowPosition = getWindowPosition(view);
if (windowPosition == null) {
return false;
}
transformMotionEventToGlobal(view, ev);
ev.offsetLocation(windowPosition[0], windowPosition[1]);
return true;
}
@Override
boolean toLocalMotionEvent(@NonNull View view, @NonNull MotionEvent ev) {
final int[] windowPosition = getWindowPosition(view);
if (windowPosition == null) {
return false;
}
ev.offsetLocation(-windowPosition[0], -windowPosition[1]);
transformMotionEventToLocal(view, ev);
return true;
}
}
}

View File

@ -0,0 +1,308 @@
/*
* 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.views;
/**
* Created by Artem on 28.01.14.
*/
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import sharedcode.turboeditor.R;
import sharedcode.turboeditor.fragment.AboutDialog;
import sharedcode.turboeditor.iab.DonationFragment;
/**
* Helper class for showing fragment dialogs.
*/
public class DialogHelper {
public static final String TAG_FRAGMENT_ABOUT = "dialog_about";
public static final String TAG_FRAGMENT_HELP = "dialog_help";
public static final String TAG_FRAGMENT_DONATION = "dialog_donate";
public static final String TAG_FRAGMENT_FEEDBACK = "dialog_feedback";
public static void showDonateDialog(Activity activity) {
showDialog(activity, DonationFragment.class, TAG_FRAGMENT_DONATION);
}
public static void showAboutDialog(Activity activity) {
showDialog(activity, AboutDialog.class, TAG_FRAGMENT_ABOUT);
}
private static void showDialog(Activity activity, Class clazz, String tag) {
FragmentManager fm = activity.getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment prev = fm.findFragmentByTag(tag);
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
try {
((DialogFragment) clazz.newInstance()).show(ft, tag);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* Helper class to implement custom dialog's design.
*/
public static class Builder {
/**
* Layout where content's layout exists in a {@link android.widget.ScrollView}.
* This is nice to display simple layout without scrollable elements such as
* {@link android.widget.ListView} or any similar. Use {@link #LAYOUT_SKELETON}
* for them.
*
* @see #LAYOUT_SKELETON
* @see #createCommonView()
* @see #wrap(int)
*/
public static final int LAYOUT_COMMON = 0;
/**
* The skeleton of dialog's layout. The only thing that is here is the custom
* view you set and the title / icon. Use it to display scrollable elements such as
* {@link android.widget.ListView}.
*
* @see #LAYOUT_COMMON
* @see #createSkeletonView()
* @see #wrap(int)
*/
public static final int LAYOUT_SKELETON = 1;
protected final Context mContext;
private Drawable mIcon;
private CharSequence mTitleText;
private CharSequence mMessageText;
private View mView;
private int mViewRes;
public Builder(Context context) {
mContext = context;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return new HashCodeBuilder(201, 17)
.append(mContext)
.append(mIcon)
.append(mTitleText)
.append(mMessageText)
.append(mViewRes)
.append(mView)
.toHashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (o == null)
return false;
if (o == this)
return true;
if (!(o instanceof Builder))
return false;
Builder builder = (Builder) o;
return new EqualsBuilder()
.append(mContext, builder.mContext)
.append(mIcon, builder.mIcon)
.append(mTitleText, builder.mTitleText)
.append(mMessageText, builder.mMessageText)
.append(mViewRes, builder.mViewRes)
.append(mView, builder.mView)
.isEquals();
}
public Builder setIcon(Drawable icon) {
mIcon = icon;
return this;
}
public Builder setTitle(CharSequence title) {
mTitleText = title;
return this;
}
public Builder setMessage(CharSequence message) {
mMessageText = message;
return this;
}
public Builder setIcon(int iconRes) {
return setIcon(iconRes == 0 ? null : mContext.getResources().getDrawable(iconRes));
}
public Builder setTitle(int titleRes) {
return setTitle(titleRes == 0 ? null : getString(titleRes));
}
public Builder setMessage(int messageRes) {
return setMessage(messageRes == 0 ? null : getString(messageRes));
}
private String getString(int stringRes) {
return mContext.getResources().getString(stringRes);
}
public Builder setView(View view) {
mView = view;
mViewRes = 0;
return this;
}
public Builder setView(int layoutRes) {
mView = null;
mViewRes = layoutRes;
return this;
}
/**
* Builds dialog's view
*
* @throws IllegalArgumentException when type is not one of defined.
* @see #LAYOUT_COMMON
* @see #LAYOUT_SKELETON
*/
public View createView(int type) {
switch (type) {
case LAYOUT_COMMON:
return createCommonView();
case LAYOUT_SKELETON:
return createSkeletonView();
default:
throw new IllegalArgumentException();
}
}
/**
* Builds view that based on simple {@link android.widget.ScrollView} container.
* This is nice to display simple layout without scrollable elements such as
* {@link android.widget.ListView} or any similar. Use {@link #createSkeletonView()}
* for them.
*
* @see #LAYOUT_COMMON
* @see #createView(int)
*/
public View createCommonView() {
// Creating skeleton layout will also try
// to add custom view. Avoid of doing it.
int customViewRes = mViewRes;
View customView = mView;
mViewRes = 0;
mView = null;
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup rootLayout = (ViewGroup) createSkeletonView();
View bodyRootView = inflater.inflate(R.layout.dialog, rootLayout, false);
ViewGroup bodyLayout = (ViewGroup) bodyRootView.findViewById(R.id.content);
TextView messageView = (TextView) bodyLayout.findViewById(R.id.message);
rootLayout.addView(bodyRootView);
// Setup content
bodyLayout.removeView(messageView);
if (!TextUtils.isEmpty(mMessageText)) {
messageView.setMovementMethod(new LinkMovementMethod());
messageView.setText(mMessageText);
bodyLayout.addView(messageView);
}
// Custom view
if (customViewRes != 0) customView = inflater.inflate(customViewRes, bodyLayout, false);
if (customView != null) bodyLayout.addView(customView);
mView = customView;
return rootLayout;
}
/**
* @see #LAYOUT_SKELETON
* @see #createView(int)
*/
public View createSkeletonView() {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup rootLayout = (ViewGroup) inflater.inflate(R.layout.dialog_skeleton, null);
TextView titleView = (TextView) rootLayout.findViewById(R.id.title);
// Setup title
if (mTitleText != null) {
titleView.setText(mTitleText);
titleView.setCompoundDrawablesWithIntrinsicBounds(mIcon, null, null, null);
} else {
// This also removes an icon.
rootLayout.removeView(titleView);
}
// Custom view
if (mViewRes != 0) mView = inflater.inflate(mViewRes, rootLayout, false);
if (mView != null) rootLayout.addView(mView);
return rootLayout;
}
public AlertDialog.Builder wrap() {
return wrap(LAYOUT_COMMON);
}
/**
* Creates view and {@link android.app.AlertDialog.Builder#setView(android.view.View) sets}
* to new {@link android.app.AlertDialog.Builder}.
*
* @param type type of container layout
* @return AlertDialog.Builder with set custom view
*/
public AlertDialog.Builder wrap(int type) {
return new AlertDialog.Builder(mContext).setView(createView(type));
}
}
}

View File

@ -22,7 +22,6 @@ package sharedcode.turboeditor.views;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -26,5 +26,5 @@
<item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_dark" />
<item android:state_focused="true" android:drawable="@color/navigation_drawer_button_inverted" />
<item android:drawable="@color/navigation_drawer_button_inverted" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@ -1,149 +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/>.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
tools:context=".activity.PreferenceAbout">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:id="@+id/imageView"
android:src="@drawable/ic_launcher"
android:layout_marginTop="10dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nome_app_turbo_editor"
android:textSize="22sp"
android:textStyle="bold"
android:layout_marginTop="10dp"
android:padding="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/changelog_text"
android:textSize="14sp"
android:textStyle="bold"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_feedback"
android:textSize="18sp"
android:textAllCaps="true"
android:padding="15dp"
android:textColor="@color/indigo"
android:textStyle="bold"
android:layout_marginTop="20dp"
android:onClick="SendFeedback"
android:clickable="true"
android:background="@drawable/item_background_holo_dark"
android:gravity="center"
android:minWidth="200dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/translate_the_app"
android:id="@+id/textView4"
android:textSize="18sp"
android:textAllCaps="true"
android:padding="15dp"
android:textColor="@color/indigo"
android:textStyle="bold"
android:onClick="OpenTranslatePage"
android:clickable="true"
android:background="@drawable/item_background_holo_dark"
android:gravity="center"
android:minWidth="200dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pro_version"
android:id="@+id/pro_version_text"
android:textSize="18sp"
android:textAllCaps="true"
android:padding="15dp"
android:textColor="@color/indigo"
android:textStyle="bold"
android:onClick="GoToProVersion"
android:clickable="true"
android:background="@drawable/item_background_holo_dark"
android:gravity="center"
android:minWidth="200dp" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_marginTop="15dp"
>
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:id="@+id/imageView5"
android:src="@drawable/ic_action_mail"
android:onClick="SendMail"
android:background="@drawable/item_background_holo_dark"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:id="@+id/imageView8"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:src="@drawable/ic_action_google_play"
android:onClick="OpenPlayStore"
android:background="@drawable/item_background_holo_dark"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:id="@+id/imageView4"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:src="@drawable/ic_action_gplus"
android:onClick="OpenGooglePlusCommunity"
android:background="@drawable/item_background_holo_dark"
android:scaleType="centerCrop" />
<ImageView
android:layout_width="45dp"
android:layout_height="45dp"
android:id="@+id/imageView6"
android:src="@drawable/ic_action_github"
android:onClick="OpenGithub"
android:background="@drawable/item_background_holo_dark"
android:scaleType="fitCenter" />
</LinearLayout>
</LinearLayout>

View File

@ -25,14 +25,81 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@id/fragment_editor"
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent"
android:gravity="center"
android:textSize="@dimen/text_size_mega_title"
android:fontFamily="sans-serif-light"
android:text="@string/open_a_file"
android:background="@null"
android:textColor="@android:color/secondary_text_dark"
android:id="@id/no_file_opened_messagge"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@id/text_editor"
android:visibility="gone">
<sharedcode.turboeditor.views.GoodScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:fillViewport="true"
android:id="@id/vertical_scroll"
android:background="@null">
<HorizontalScrollView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:scrollbars="horizontal"
android:fillViewport="true"
android:id="@id/horizontal_scroll"
android:background="@null">
<view
class="sharedcode.turboeditor.activity.BaseHomeActivity$Editor"
android:id="@id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:bufferType="spannable"
android:textCursorDrawable="@null"
android:cursorVisible="true"
android:gravity="top|start"
android:singleLine="false"
android:background="@null"
android:text=""
android:textIsSelectable="true" />
</HorizontalScrollView>
</sharedcode.turboeditor.views.GoodScrollView>
<com.faizmalkani.floatingactionbutton.FloatingActionButton
android:id="@+id/fabNext"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:visibility="invisible"/>
<com.faizmalkani.floatingactionbutton.FloatingActionButton
android:id="@+id/fabPrev"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="100dp"
android:layout_marginEnd="90dp"
android:visibility="invisible"/>
</FrameLayout>
<fragment android:layout_width="@dimen/navigation_drawer_width"
android:layout_height="match_parent"
android:name="sharedcode.turboeditor.fragment.NavigationDrawerListFragment"
android:name="sharedcode.turboeditor.fragment.NavigationDrawer"
android:id="@id/drawer_files"
android:layout_gravity="start"
tools:layout="@layout/fragment_navigation_drawer"

View File

@ -28,11 +28,7 @@
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:smoothScrollbar="true"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"/>
android:smoothScrollbar="true"/>
<com.faizmalkani.floatingactionbutton.FloatingActionButton
android:id="@+id/fabbutton"

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
~
~ This program 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 2
~ 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 General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program; if not, write to the Free Software
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
~ MA 02110-1301, USA.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical">
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?textAppearanceDialogMessage" />
</LinearLayout>
</ScrollView>

View File

@ -19,7 +19,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
~
~ This program 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 2
~ 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 General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program; if not, write to the Free Software
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
~ MA 02110-1301, USA.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:showDividers="middle"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:minHeight="48dp"
style="?textAppearanceDialogTitle"/>
</LinearLayout>

View File

@ -0,0 +1,89 @@
<?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
style="?textAppearanceDialogMessage"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp">
<TextView
android:id="@+id/error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_gravity="center"
android:maxWidth="200dp"
android:background="?android:listChoiceBackgroundIndicator"
android:drawablePadding="4dp"
android:padding="8dp"
android:fontFamily="sans-serif-condensed"
android:textStyle="italic"
android:gravity="center_horizontal" />
<ProgressBar
android:id="@android:id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_gravity="center"
android:padding="8dp" />
<GridView
android:id="@+id/grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:scrollbarStyle="outsideOverlay"
android:stretchMode="columnWidth"
android:numColumns="auto_fit" />
</FrameLayout>
</LinearLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2014 AChep@xda <artemchep@gmail.com>
~
~ This program 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 2
~ 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 General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program; if not, write to the Free Software
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
~ MA 02110-1301, USA.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:padding="8dip"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="54sp"
android:fontFamily="sans-serif-light"
android:textSize="32sp"
android:textColor="@color/donation_normal"/>
<TextView
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:fontFamily="sans-serif-condensed"
android:textAllCaps="true"
android:textSize="12sp"
android:maxLines="2"
android:ellipsize="end"/>
</LinearLayout>

View File

@ -1,78 +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/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<sharedcode.turboeditor.views.GoodScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:fillViewport="true"
android:id="@id/vertical_scroll"
android:background="@null">
<HorizontalScrollView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:scrollbars="horizontal"
android:fillViewport="true"
android:id="@id/horizontal_scroll"
android:background="@null">
<view
class="sharedcode.turboeditor.fragment.EditorFragment$Editor"
android:id="@id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:bufferType="spannable"
android:textCursorDrawable="@null"
android:cursorVisible="true"
android:gravity="top|start"
android:singleLine="false"
android:background="@null"
android:text=""
android:textIsSelectable="true" />
</HorizontalScrollView>
</sharedcode.turboeditor.views.GoodScrollView>
<com.faizmalkani.floatingactionbutton.FloatingActionButton
android:id="@+id/fabNext"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
android:visibility="invisible"/>
<com.faizmalkani.floatingactionbutton.FloatingActionButton
android:id="@+id/fabPrev"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="16dp"
android:layout_marginRight="100dp"
android:layout_marginEnd="90dp"
android:visibility="invisible"/>
</FrameLayout>

View File

@ -31,16 +31,11 @@
android:orientation="vertical"
>
<View
android:background="@color/divider_inverted"
android:layout_width="match_parent"
android:layout_height="@dimen/line_dimension"/>
<TextView
android:id="@id/drawer_button_go_pro"
android:layout_width="match_parent"
android:layout_height="72dp"
android:text="@string/pro_version"
android:text="@string/donate"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingLeft="20dp"

View File

@ -20,6 +20,13 @@
<changelog bulletedList="true">
<changelogversion versionName="1.13" changeDate="Oct 17, 2014">
<changelogtext>Turbo Editor is a free and open source app. Now you can show your appreciation and support development by donating :)</changelogtext>
<changelogtext>New visual changes</changelogtext>
<changelogtext>New save dialog when you are about to close a file</changelogtext>
<changelogtext>Many enchantments and fixes</changelogtext>
</changelogversion>
<changelogversion versionName="1.12" changeDate="Oct 9, 2014">
<changelogtext>Many enchantments and fixes</changelogtext>
</changelogversion>

View File

@ -20,7 +20,6 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">64dp</dimen>
<dimen name="activity_vertical_margin">64dp</dimen>
<!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp:
https://developer.android.com/design/patterns/navigation-drawer.html -->

View File

@ -18,12 +18,12 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="@dimen/text_size_mega_title"
android:fontFamily="sans-serif-light"
android:text="@string/open_a_file"
android:background="@null"
android:textColor="@android:color/secondary_text_dark"/>
<resources>
<declare-styleable name="Theme">
<!-- Dialog styles -->
<attr name="textAppearanceDialogTitle" format="reference"/>
<attr name="textAppearanceDialogMessage" format="reference"/>
<attr name="textAppearanceDialogInfo" format="reference"/>
</declare-styleable>
</resources>

View File

@ -61,4 +61,7 @@
<color name="item_selected">#40969696</color>
<color name="gradient_start">#50000000</color>
<color name="gradient_light_start">#30000000</color>
<color name="donation_normal">#b6cc45</color>
<color name="donation_purchased">#DFED47</color>
</resources>

View File

@ -19,8 +19,8 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">0dp</dimen>
<dimen name="activity_vertical_margin">0dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<!-- Per the design guidelines, navigation drawers should be between 240dp and 320dp:
https://developer.android.com/design/patterns/navigation-drawer.html -->

View File

@ -50,7 +50,6 @@
<item type="id" name="im_donate_a_coffee"/>
<!-- layout item ids -->
<item type="id" name="drawer_layout"/>
<item type="id" name="fragment_editor"/>
<item type="id" name="editor"/>
<item type="id" name="vertical_scroll"/>
<item type="id" name="horizontal_scroll"/>
@ -59,6 +58,9 @@
<item type="id" name="drawer_settings"/>
<item type="id" name="button_remove_from_list"/>
<item type="id" name="text_editor"/>
<item type="id" name="no_file_opened_messagge"/>
<item type="id" name="regex_check"/>
<item type="id" name="replace_check"/>
<item type="id" name="match_case_check"/>

View File

@ -30,7 +30,8 @@
<string name="file">File</string>
<string name="folder">Folder</string>
<string name="light_theme">Light Theme</string>
<string name="goto_line">Go to Line</string>
<string name="goto_line">Go to Line</string>
<string name="goto_page">Go to Page…</string>
<string name="find">Find</string>
<string name="replace">Replace</string>
<string name="share">Share</string>
@ -46,12 +47,11 @@
<string name="previous">Previous</string>
<string name="please_wait">Please wait…</string>
<string name="occurrences_found">%s occurrences was found</string>
<string name="app_version">Version %s</string>
<string name="app_version_new">v%s</string>
<string name="translate_the_app">Translate</string>
<string name="changelog">Changelog</string>
<string name="match_case">Match case</string>
<string name="long_click_for_more_options">Long click for more options</string>
<string name="pro_version">Pro version</string>
<string name="auto_save">Auto save</string>
<string name="read_only">Read only</string>
<string name="send_error_reports">Send error reports</string>
@ -59,116 +59,18 @@
<string name="split_text_if_too_long">Split the text if too long</string>
<string name="ignore_back_button">Ignore back button</string>
<string name="donate">Donate</string>
<!-- turbo client -->
<string name="aggiungi_account">New account</string>
<string name="attiva">Active</string>
<string name="cancella">Delete</string>
<string name="cancellazione">Deleting files&#8230;</string>
<string name="caricamento">Loading&#8230;</string>
<string name="cartella_locale_corrente">Current local folder</string>
<string name="chiave_privata">Private Key</string>
<string name="chiaro">Light</string>
<string name="codifica">Encoding</string>
<string name="condividi">Share</string>
<string name="crea_cartella_locale">New local folder</string>
<string name="crea_cartella_remota">New remote folder</string>
<string name="crea_file_remoto">New remote file</string>
<string name="new_local_file">New local file</string>
<string name="disconneti">Disconnect</string>
<string name="default_local_folder">Default local folder</string>
<string name="dove_scaricare">Where to download?</string>
<string name="download">Download</string>
<string name="download_completato">Download completed</string>
<string name="duplicate">Duplicate</string>
<string name="fatto">Done</string>
<string name="home">Home</string>
<string name="host">Host</string>
<string name="info">Info</string>
<string name="locale">Local</string>
<string name="log_in">Logging in&#8230;</string>
<string name="modifica">Edit</string>
<string name="muovi">Move</string>
<string name="nascondi">Hide</string>
<string name="nome_app">Turbo Client</string>
<string name="nome_app_turbo_editor">Turbo Editor</string>
<string name="nome_utente">Username</string>
<string name="passiva">Passive</string>
<string name="passphrase">Passphrase</string>
<string name="password">Password</string>
<string name="password_summary">Leave it empty to prompt for it every session</string>
<string name="porta">Port</string>
<string name="preferenze">Preferences</string>
<string name="remoto">Remote</string>
<string name="riavva_per_tema">To change the theme, restart the application</string>
<string name="rinomina">Rename</string>
<string name="root">Default remote folder</string>
<string name="salva">Save</string>
<string name="scuro">Dark</string>
<string name="seleziona">Select</string>
<string name="seleziona_account">Select an account</string>
<string name="sicuro">Are you sure?</string>
<string name="something_failed">Something failed</string>
<string name="skip_same_file">Do not transfer same file</string>
<string name="tema_app">App theme</string>
<string name="tipo_connessione">Connection type</string>
<string name="tipo_protocollo">Protocol type</string>
<string name="un_altra_cartella">Another folder</string>
<string name="use_passphrase">Use a passphrase</string>
<string name="upload">Upload</string>
<string name="upload_completato">Upload completed</string>
<string name="what_to_do">What do you want to do?</string>
<string name="menu_syntax_highlight">Syntax highlight</string>
<string name="testo_indietro">Undo</string>
<string name="testo_rifai">Redo</string>
<string name="sync">Sync</string>
<string name="remote_folder_to_sync">Remote folder to sync</string>
<string name="local_folder_to_sync">Local folder to sync</string>
<string name="vota">Rate</string>
<string name="inapp_unavailable">Cannot contact Google Play</string>
<string name="inapp_description">Support the development of other great features.</string>
<string name="upgrade_premium">Upgrade to Premium</string>
<string name="upgrade_premium_summary">Upgrade to Premium and support the development of Turbo Client!</string>
<string name="download_unlocked_version">Download unlocked version</string>
<string name="inapp_second_description">What is Turbo Client worth to you? Set your price! </string>
<string name="inapp_first_description">Upgrade to unlock this features:</string>
<string name="inapp_item_openandeditfiles">Power to open and modify any type of file.</string>
<string name="inapp_item_backup_service">Backup service to backup and restore your data safely. </string>
<string name="inapp_unlock_features">Unlock the Premium features</string>
<string name="inapp_seconditem_description">I really like this app!</string>
<string name="inapp_thirditem_description">I love this app!</string>
<string name="backup_accounts">Backup the accounts</string>
<string name="restore_accounts">Restore the accounts</string>
<string name="share_accounts">Backup and share the accounts</string>
<string name="importing_accounts">Importing the accounts&#8230;</string>
<string name="exporting_accounts">Exporting the accounts...</string>
<string name="backup_not_found">No backups found</string>
<string name="err_cant_open_the_file">Cannot open the file</string>
<string name="err_temp_folder_doesnt_exist">Temporary folder does not exist</string>
<string name="err_occured">An error occurred</string>
<string name="ui_ux">UI</string>
<string name="remove">Remove</string>
<string name="modification_date">Modification date</string>
<string name="name">Name</string>
<string name="size">Size</string>
<string name="sort">Sort</string>
<string name="open">Open</string>
<string name="file_modified">The file %1$s was modified, do you want to upload it?</string>
<string name="file_saved_with_success">The file %1$s was saved with success!</string>
<string name="create_new_account">Create a new account</string>
<string name="create_new_account_to_start">Create a new account to start.</string>
<string name="type">Type</string>
<string name="send_feedback">Send feedback</string>
<string name="copy_url">Copy URL</string>
<string name="cut">Cut</string>
<string name="paste">paste</string>
<string name="advanced">Advanced</string>
<string name="auto">Auto</string>
<string name="bytes">Bytes</string>
<string name="unit_measurement_for_file_size">Unit of measurement for file size</string>
<string name="open_source_license">Open Source licenses</string>
<string name="open_source_license_summary">Show open source licenses</string>
<string name="open_a_file">Open a file</string>
<string name="open_this_time_only">Open this time only</string>
<string name="change_list_type">Change the list type</string>
<string name="no">No</string>
</resources>

View File

@ -0,0 +1,40 @@
<?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/>.
-->
<resources>
<string name="close">Close</string>
<!-- About -->
<string name="about_action">About</string>
<string name="about_title" translatable="false"><![CDATA[
<b>%1$s</b>&nbsp;<font color="#888888">v%2$s</font>
]]></string>
<string name="about_message"><![CDATA[
Turbo Editor is a free and <a href="http://github.com/vmihalachi/turbo-editor">open source</a> app.
Copyright 2013-2014 <a href="https://plus.google.com/+VladMihalachi">Vlad Mihalachi</a>. All Rights Reserved.<br/>
<br/>
Many thanks to all who
<a href="http://crowdin.net/project/turbo-client">helped with translations</a> or
donated to me</a>.<br/>
<br/>
If you want to send feedback here is the <a href="http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016">XDA thread</a>
]]></string>
</resources>

View File

@ -0,0 +1,45 @@
<?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/>.
-->
<resources>
<string name="donation_action">Donate</string>
<string name="donation_title">Donate to developer</string>
<string name="donation_info"><![CDATA[
Turbo Editor is a free and <a href="http://github.com/vmihalachi/turbo-editor/">open source</a> app.
You can show your appreciation and support development by donating:
]]></string>
<string name="donation_item_label" translatable="false">﹩%1$s</string>
<string name="donation_item_bought">You\'ve donated for this item already.</string>
<string name="donation_2">An ice cream</string>
<string name="donation_4">Cup of coffee</string>
<string name="donation_10">Electricity bills</string>
<string name="donation_20">The right pillow</string>
<string name="donation_50">Solid-state drive</string>
<string name="donation_99">Sound system</string>
<string name="donation_error_iab_setup">Failed to setup in-app-billing service!</string>
<string name="donation_no_responsibility">Notice that Google is not responsible for that payments method.</string>
<string name="paypal" translatable="false">PayPal</string>
</resources>

View File

@ -0,0 +1,104 @@
<?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/>.
-->
<resources>
<string name="change_list_type">Change the list type</string>
<string name="copy_url">Copy URL</string>
<string name="cut">Cut</string>
<string name="paste">Paste</string>
<string name="advanced">Advanced</string>
<string name="auto">Auto</string>
<string name="bytes">Bytes</string>
<string name="unit_measurement_for_file_size">Unit of measurement for file size</string>
<string name="create_new_account">Create a new account</string>
<string name="create_new_account_to_start">Create a new account to start.</string>
<string name="type">Type</string>
<string name="file_modified">The file %1$s was modified, do you want to upload it?</string>
<string name="sync">Sync</string>
<string name="remote_folder_to_sync">Remote folder to sync</string>
<string name="local_folder_to_sync">Local folder to sync</string>
<string name="vota">Rate</string>
<string name="backup_accounts">Backup the accounts</string>
<string name="restore_accounts">Restore the accounts</string>
<string name="share_accounts">Backup and share the accounts</string>
<string name="importing_accounts">Importing the accounts&#8230;</string>
<string name="exporting_accounts">Exporting the accounts…</string>
<string name="backup_not_found">No backups found</string>
<string name="err_cant_open_the_file">Cannot open the file</string>
<string name="err_temp_folder_doesnt_exist">Temporary folder does not exist</string>
<string name="err_occured">An error occurred</string>
<string name="ui_ux">UI</string>
<string name="remove">Remove</string>
<string name="modification_date">Modification date</string>
<string name="name">Name</string>
<string name="size">Size</string>
<string name="sort">Sort</string>
<string name="scuro">Dark</string>
<string name="seleziona">Select</string>
<string name="seleziona_account">Select an account</string>
<string name="sicuro">Are you sure?</string>
<string name="something_failed">Something failed</string>
<string name="skip_same_file">Do not transfer same file</string>
<string name="tema_app">App theme</string>
<string name="tipo_connessione">Connection type</string>
<string name="tipo_protocollo">Protocol type</string>
<string name="un_altra_cartella">Another folder</string>
<string name="use_passphrase">Use a passphrase</string>
<string name="upload">Upload</string>
<string name="upload_completato">Upload completed</string>
<string name="what_to_do">What do you want to do?</string>
<string name="remoto">Remote</string>
<string name="riavva_per_tema">To change the theme, restart the application</string>
<string name="rinomina">Rename</string>
<string name="root">Default remote folder</string>
<string name="nome_utente">Username</string>
<string name="passiva">Passive</string>
<string name="passphrase">Passphrase</string>
<string name="password">Password</string>
<string name="password_summary">Leave it empty to prompt for it every session</string>
<string name="porta">Port</string>
<string name="locale">Local</string>
<string name="log_in">Logging in…</string>
<string name="modifica">Edit</string>
<string name="muovi">Move</string>
<string name="nascondi">Hide</string>
<string name="nome_app">Turbo Client</string>
<string name="host">Host</string>
<string name="crea_cartella_locale">New local folder</string>
<string name="crea_cartella_remota">New remote folder</string>
<string name="crea_file_remoto">New remote file</string>
<string name="new_local_file">New local file</string>
<string name="disconneti">Disconnect</string>
<string name="default_local_folder">Default local folder</string>
<string name="dove_scaricare">Where to download?</string>
<string name="download">Download</string>
<string name="download_completato">Download completed</string>
<string name="duplicate">Duplicate</string>
<string name="fatto">Done</string>
<string name="home">Home</string>
<string name="chiaro">Light</string>
<string name="aggiungi_account">New account</string>
<string name="attiva">Active</string>
<string name="cancella">Delete</string>
<string name="cancellazione">Deleting files…</string>
<string name="caricamento">Loading…</string>
<string name="cartella_locale_corrente">Current local folder</string>
<string name="chiave_privata">Private Key</string>
</resources>

View File

@ -19,14 +19,14 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo">
<style name="AppThemeBaseDark" parent="android:Theme.Holo">
</style>
<!-- Dialog -->
<item name="textAppearanceDialogTitle">@style/TextAppearance.Dialog.Title</item>
<item name="textAppearanceDialogMessage">@style/TextAppearance.Dialog.Message</item>
<item name="textAppearanceDialogInfo">@style/TextAppearance.Dialog.Info</item>
<style name="AppTheme.Light.Editor" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:windowBackground">@null</item>
<item name="android:windowBackground">@color/window_background</item>
<item name="android:actionBarStyle">@style/ActionBar.Dark</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:actionBarSize">@dimen/action_bar_height</item>
@ -34,8 +34,13 @@
<item name="android:homeAsUpIndicator">@drawable/ic_ab_up_compat</item>
</style>
<style name="AppTheme.Light" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
<style name="AppThemeBaseLight" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Dialog -->
<item name="textAppearanceDialogTitle">@style/TextAppearance.Dialog.Title</item>
<item name="textAppearanceDialogMessage">@style/TextAppearance.Dialog.Message</item>
<item name="textAppearanceDialogInfo">@style/TextAppearance.Dialog.Info</item>
<item name="android:windowBackground">@color/window_background_light</item>
<item name="android:actionBarStyle">@style/ActionBar.Dark</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
@ -44,14 +49,8 @@
<item name="android:homeAsUpIndicator">@drawable/ic_ab_up_compat</item>
</style>
<style name="AppTheme.Dark" parent="android:Theme.Holo">
<!-- Customize your theme here. -->
<item name="android:windowBackground">@color/window_background</item>
<item name="android:actionBarStyle">@style/ActionBar.Dark</item>
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
<item name="android:actionBarSize">@dimen/action_bar_height</item>
<item name="android:windowContentOverlay">@drawable/actionbar_shadow</item>
<item name="android:homeAsUpIndicator">@drawable/ic_ab_up_compat</item>
<style name="AppThemeEditorDark" parent="AppThemeBaseDark">
<item name="android:windowBackground">@null</item>
</style>
<style name="ActionBar.Light" parent="android:Widget.Holo.Light.ActionBar">
@ -110,4 +109,19 @@
<item name="android:src">@drawable/ic_ab_overflow_compat</item>
</style>
<style name="TextAppearance.Dialog.Title" parent="android:TextAppearance.Holo.Large">
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textSize">24sp</item>
</style>
<style name="TextAppearance.Dialog.Message" parent="android:TextAppearance.Holo.Small">
<item name="android:lineSpacingMultiplier">1.2</item>
<item name="android:fontFamily">sans-serif-light</item>
<item name="android:textSize">16sp</item>
</style>
<style name="TextAppearance.Dialog.Info" parent="android:TextAppearance.Holo.Small">
<item name="android:fontFamily">sans-serif-condensed</item>
</style>
</resources>