Compare commits

...

65 Commits

Author SHA1 Message Date
Jan-Lukas Else ed27844fa9 1.5.5 2020-10-31 22:16:59 +01:00
Jan-Lukas Else e99b487218 1.5.4 2019-06-07 07:51:32 +02:00
Jan-Lukas Else 792bb627c8 Update dependencies 2019-06-07 07:40:12 +02:00
Jan-Lukas Else e47cfe7d38 Update Material Dialogs 2019-03-24 15:31:30 +01:00
dependabot[bot] dda25c44ec Bump gradle from 3.3.1 to 3.3.2 (#22)
Bumps gradle from 3.3.1 to 3.3.2.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-24 15:20:41 +01:00
dependabot[bot] 0d80011ac3 Bump input from 2.0.0 to 2.0.3 (#21)
Bumps input from 2.0.0 to 2.0.3.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 18:32:41 +01:00
dependabot[bot] 02fb6bb3ae Bump core from 2.0.0 to 2.0.3 (#20)
Bumps core from 2.0.0 to 2.0.3.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 18:27:50 +01:00
dependabot[bot] a3343e1f30 Bump Android-AdvancedWebView from 3.1.3 to v3.2.0 (#15)
Bumps [Android-AdvancedWebView](https://github.com/delight-im/Android-AdvancedWebView) from 3.1.3 to v3.2.0.
- [Release notes](https://github.com/delight-im/Android-AdvancedWebView/releases)
- [Commits](https://github.com/delight-im/Android-AdvancedWebView/compare/v3.1.3...v3.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-03-01 18:10:10 +01:00
Jan-Lukas Else bf384a8f59 1.5.3 2019-02-10 10:59:06 +01:00
Jan-Lukas Else 1d00e34219 Small fix 2019-02-10 10:53:48 +01:00
Jan-Lukas Else 3946d21e68 Improve HTML 2019-02-10 10:46:36 +01:00
dependabot[bot] 9857315274 Bump input from 2.0.0-rc11 to 2.0.0 (#12)
Bumps input from 2.0.0-rc11 to 2.0.0.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-10 10:21:53 +01:00
dependabot[bot] cfe2950b34 Bump core from 2.0.0-rc11 to 2.0.0 (#11)
Bumps core from 2.0.0-rc11 to 2.0.0.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-10 10:19:12 +01:00
dependabot[bot] b989b775b4 Bump kotlin_version from 1.3.20 to 1.3.21 (#14)
Bumps `kotlin_version` from 1.3.20 to 1.3.21.

Updates `kotlin-gradle-plugin` from 1.3.20 to 1.3.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.20...v1.3.21)

Updates `kotlin-stdlib` from 1.3.20 to 1.3.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.20...v1.3.21)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-10 10:18:58 +01:00
dependabot[bot] 5c6476e99a Bump gradle from 3.3.0 to 3.3.1 (#13)
Bumps gradle from 3.3.0 to 3.3.1.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-10 10:18:41 +01:00
dependabot[bot] 35bada0699 Bump input from 2.0.0-rc10 to 2.0.0-rc11 (#10)
Bumps input from 2.0.0-rc10 to 2.0.0-rc11.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-06 08:23:26 +01:00
dependabot[bot] cb7f3b7dd1 Bump core from 2.0.0-rc10 to 2.0.0-rc11 (#9)
Bumps core from 2.0.0-rc10 to 2.0.0-rc11.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-06 07:58:24 +01:00
Jan-Lukas Else 2957a2f250 Fixes and improvements 2019-02-05 22:18:51 +01:00
Jan-Lukas Else c93a7ae575 Upgrade Fuel to 2.0.1 (also bump min sdk to 19 - required for update) 2019-02-05 20:58:42 +01:00
Jan-Lukas Else b17d8802a5 Update Material Dialogs Input library 2019-02-05 20:21:45 +01:00
dependabot[bot] 7ef293950e Bump core from 2.0.0-rc7 to 2.0.0-rc10 (#7)
Bumps core from 2.0.0-rc7 to 2.0.0-rc10.

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-05 20:12:10 +01:00
dependabot[bot] 07539e18d7 Bump kotlin_version from 1.3.11 to 1.3.20 (#8)
Bumps `kotlin_version` from 1.3.11 to 1.3.20.

Updates `kotlin-gradle-plugin` from 1.3.11 to 1.3.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.11...v1.3.20)

Updates `kotlin-stdlib` from 1.3.11 to 1.3.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.11...v1.3.20)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-05 20:11:43 +01:00
Licaon_Kter cb6291be6b Remove uneeded reps (#4)
...this also blocked F-Droid.
2019-02-05 19:55:02 +01:00
Jan-Lukas Else 57140d34e2 1.5.2 2019-01-23 17:36:46 +01:00
florian19982 207ef7fdf0 Added i18n for German (#3) 2018-10-06 20:40:34 +02:00
Jan-Lukas Else e4443b95dd Migrate to AndroidX 2018-09-22 08:18:57 +02:00
Jan-Lukas Else fc561cf3e5 Update Gradle wrapper 2018-09-13 21:27:08 +02:00
Jan-Lukas Else 0ec5066b18 Dependency upgrades, basic bug fixing and min API bump 2018-09-11 22:24:39 +02:00
Jan-Lukas Else 041137004c Update Gradle wrapper 2018-09-11 20:51:33 +02:00
Jan-Lukas Else b2065a0fcd
Merge pull request #2 from Poussinou/patch-1
Update README.md
2018-08-20 07:46:29 +02:00
Poussinou 3e16b85e94
Update README.md 2018-08-11 15:25:46 +02:00
Jan-Lukas Else 4ef87824a8 1.5.1 2018-07-29 08:47:28 +02:00
Jan-Lukas Else 58490c8096 Update libraries 2018-07-29 08:39:18 +02:00
Jan-Lukas Else b80f0a0088 Update libraries 2018-05-30 17:54:33 +02:00
Jan-Lukas Else 95fbde01e6 Update Gradle Wrapper 2018-05-30 17:46:28 +02:00
Jan-Lukas Else ac5335af86 1.5 2018-02-14 19:24:11 +01:00
Jan-Lukas Else 1a5c508414 Fix displaying author 2018-02-14 19:23:45 +01:00
Jan-Lukas Else f63385d1d1 Fix displaying author 2018-02-14 19:10:58 +01:00
Jan-Lukas Else 62bfb2059f Remove reference to unused library 2018-02-14 19:02:20 +01:00
Jan-Lukas Else c59258af1e Remove editor border 2018-02-14 19:00:37 +01:00
Jan-Lukas Else bddc725fcb Change color scheme 2018-02-14 18:57:18 +01:00
Jan-Lukas Else 72dd10a14a Add Login help, polish menu 2018-02-14 18:51:20 +01:00
Jan-Lukas Else 3e704f3080 Enable login 2018-02-14 18:13:55 +01:00
Jan-Lukas Else d82166935f Add API method to login (untested) 2018-02-12 21:56:36 +01:00
Jan-Lukas Else e9f035e260 Add some style options to editor, remove useless strings 2018-02-12 19:54:07 +01:00
Jan-Lukas Else f8bc76157a Viewer class 2018-02-12 19:36:19 +01:00
Jan-Lukas Else 459a1b98f8 Fix some editor bugs 2018-02-12 18:18:16 +01:00
Jan-Lukas Else 4ccb45df39 Move image upload to JS part 2018-02-12 17:59:30 +01:00
Jan-Lukas Else fe09e464bf Update Fuel library 2018-02-12 15:45:01 +01:00
Jan-Lukas Else f1e2527be0 1.4.1 2018-02-06 22:02:28 +01:00
Jan-Lukas Else 70d918a884 Use same cdn in viewer as in editor 2018-02-06 22:00:01 +01:00
Jan-Lukas Else 5ca3e9658f Change my homepage url 2018-02-06 21:55:06 +01:00
Jan-Lukas Else 8e946f6f38 Fix editor and make it faster 2018-02-06 21:54:21 +01:00
Jan-Lukas Else 6f4e211085 1.4 2018-02-06 20:19:48 +01:00
Jan-Lukas Else 4c45407522 Update used libraries 2018-02-06 20:14:32 +01:00
Jan-Lukas Else b074461076 Use POST request for everything (should fix a lot of bugs) 2018-02-06 20:12:01 +01:00
Jan-Lukas Else 0eb13a09ab Fix crash with bookmarks & make bookmarks impl better (still ugly, but ok) 2018-02-06 19:51:21 +01:00
Jan-Lukas Else 5d109b88b2 Fix image upload 2018-02-04 22:59:48 +01:00
Jan-Lukas Else b304de3dbb Replace old API wrapper 2018-02-04 22:49:32 +01:00
Jan-Lukas Else 835300c11e Write a new api wrapper (that was hard work) 2018-02-04 21:42:56 +01:00
Jan-Lukas Else a4b69561a1 Update dependencies 2018-02-04 15:49:18 +01:00
Jan-Lukas Else 9198d11d1b Update Gradle version 2018-02-04 15:40:02 +01:00
Jan-Lk Else 2ad784dba7 Fixed the bad crashes 2017-09-13 22:03:05 +02:00
Jan-Lk Else 986c5c4042 Fix compile errors 2017-09-13 21:24:06 +02:00
Jan-Lk Else a38e7a90f0 Update dependencies 2017-09-13 21:10:31 +02:00
26 changed files with 980 additions and 626 deletions

View File

@ -8,6 +8,13 @@ For more information about Telegra.ph visit https://telegram.org/blog/instant-vi
Please take care, that this app might be unstable due to it's early development state!
[<img src="https://f-droid.org/badge/get-it-on.png"
alt="Get it on F-Droid"
height="90">](https://f-droid.org/packages/telegra.ph/)
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png"
alt="Get it on Google Play"
height="90">](https://play.google.com/store/apps/details?id=telegra.ph)
## LICENSE
```

View File

@ -2,19 +2,19 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 26
buildToolsVersion "26.0.0"
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "telegra.ph"
minSdkVersion 15
targetSdkVersion 26
versionCode 9
versionName "1.3.0"
resConfigs "en"
minSdkVersion 19
targetSdkVersion 30
versionCode 18
versionName "1.5.5"
resConfigs "en", "de", "es", "tr", "ru"
}
buildTypes {
debug {
minifyEnabled true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
applicationIdSuffix '.debug'
versionNameSuffix ' debug'
@ -24,19 +24,19 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
compile 'com.android.support:appcompat-v7:25.4.0'
compile 'com.android.support:recyclerview-v7:25.4.0'
compile 'com.android.support:support-v13:25.4.0'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile 'com.github.delight-im:Android-AdvancedWebView:v3.0.0'
compile 'com.afollestad.material-dialogs:core:0.9.4.5'
compile 'com.afollestad.material-dialogs:commons:0.9.4.5'
compile 'pub.devrel:easypermissions:0.4.2'
compile('com.afollestad:bridge:5.1.2') {
exclude group: 'org.json', module: 'json'
exclude group: 'com.intellij', module: 'annotations'
}
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.github.delight-im:Android-AdvancedWebView:3.2.1'
implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.afollestad.material-dialogs:input:3.3.0'
implementation 'com.github.kittinunf.fuel:fuel:2.3.0'
implementation 'com.github.kittinunf.fuel:fuel-android:2.3.0'
implementation 'com.github.kittinunf.fuel:fuel-json:2.3.0'
}

View File

@ -32,6 +32,9 @@
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="telegra.ph" />
<data android:host="graph.org" />
<data android:host="edit.telegra.ph" />
<data android:host="edit.graph.org" />
</intent-filter>
</activity>
</application>

View File

@ -1,74 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
rel="stylesheet">
<link href="https://rawgit.com/summernote/summernote/master/dist/summernote.css"
rel="stylesheet">
<style> * { max-width: 100%; height: auto; word-break: break-all; word-break: break-word; }</style>
</head>
<body>
<div id="summernote" style="width:100%;height:100%"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://rawgit.com/summernote/summernote/master/dist/summernote.min.js"></script>
<script>
function domToNode(domNode) {
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote-lite.css"
rel="stylesheet"
/>
<style>
* {
max-width: 100% !important;
height: auto;
word-break: break-all;
word-break: break-word;
}
#summernote {
width: 100%;
height: 100%;
}
.note-editor {
border: none !important;
}
</style>
</head>
<body>
<div id="summernote"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/summernote/0.8.18/summernote-lite.min.js"></script>
<script>
function domToNode(domNode) {
if (domNode.nodeType == domNode.TEXT_NODE) {
return domNode.data;
return domNode.data;
}
if (domNode.nodeType != domNode.ELEMENT_NODE) {
return false;
return false;
}
var nodeElement = {};
nodeElement.tag = domNode.tagName.toLowerCase();
for (var i = 0; i < domNode.attributes.length; i++) {
var attr = domNode.attributes[i];
if (attr.name == 'href' || attr.name == 'src') {
if (!nodeElement.attrs) {
nodeElement.attrs = {};
}
nodeElement.attrs[attr.name] = attr.value;
var attr = domNode.attributes[i];
if (attr.name == "href" || attr.name == "src") {
if (!nodeElement.attrs) {
nodeElement.attrs = {};
}
nodeElement.attrs[attr.name] = attr.value;
}
}
if (domNode.childNodes.length > 0) {
nodeElement.children = [];
for (var ii = 0; ii < domNode.childNodes.length; ii++) {
var child = domNode.childNodes[ii];
nodeElement.children.push(domToNode(child));
}
nodeElement.children = [];
for (var ii = 0; ii < domNode.childNodes.length; ii++) {
var child = domNode.childNodes[ii];
nodeElement.children.push(domToNode(child));
}
}
return nodeElement;
}
}
function getNodeJson() {
window.android.getText(JSON.stringify(domToNode(document.getElementsByClassName('note-editable')[0]).children));
}
</script>
<script>
$(document).ready(function () {
$('#summernote').summernote({
height: 1200,
focus: true,
placeholder: '',
toolbar: [
// [groupName, [list of button]]
['style', ['bold', 'italic']],
['para', ['ul', 'ol']],
['insert', ['link']],
['history', ['undo', 'redo']],
['other', ['codeview']]
],
callbacks: {
onInit: function (e) {
$("#summernote").summernote("fullscreen.toggle");
}
function getNodeJson() {
window.android.getText(
JSON.stringify(
domToNode(document.getElementsByClassName("note-editable")[0])
.children
)
);
}
function uploadImage(file) {
data = new FormData();
data.append("FileUpload", file);
$.ajax({
data: data,
type: "POST",
url: "https://telegra.ph/upload",
cache: false,
contentType: false,
processData: false,
success: function(data) {
if (data) {
$("#summernote").summernote("insertImage", data[0].src);
}
}
});
});
</script>
</body>
</html>
}
$(document).ready(function() {
$("#summernote").summernote({
focus: true,
placeholder: "Start writing...",
styleTags: ["p", "h3", "h4", "blockquote", "pre"],
toolbar: [
["style", ["style", "bold", "italic", "underline", "clear"]],
["para", ["ul", "ol"]],
["insert", ["link", "picture", "hr"]],
["history", ["undo", "redo"]],
["other", ["codeview"]]
],
callbacks: {
onInit: function(e) {
$("#summernote").summernote("fullscreen.toggle");
},
onImageUpload: function(files) {
uploadImage(files[0]);
}
}
});
});
function setContent(content) {
$("#summernote").summernote("code", content);
}
</script>
</body>
</html>

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css"
/>
<style>
* {
max-width: 100% !important;
height: auto;
word-break: break-all;
word-break: break-word;
}
</style>
</head>
<body>
<main role="main" class="container">
<div id="viewerTitle" class="mt-3"></div>
<div id="viewerAuthor"></div>
<div id="viewerViews"></div>
<div id="viewerContent"></div>
</main>
<script>
function setTitle(title) {
document.getElementById("viewerTitle").innerHTML =
"<h1>" + title + "</h1>";
}
function setAuthor(author, url) {
var viewerAuthor = document.getElementById("viewerAuthor");
if (author && url && author.length > 0 && url.length > 0)
viewerAuthor.innerHTML =
'By <a href="' + url + '">' + author + "</a><br>";
else if (author && author.length > 0)
viewerAuthor.innerHTML = "By " + author + "<br>";
else if (url && url.length > 0)
viewerAuthor.innerHTML =
'By <a href="' + url + '"><i>Author</i></a><br>';
else viewerAuthor.innerHTML = "";
}
function setViews(views) {
document.getElementById("viewerViews").innerHTML =
views + " times viewed<br><br>";
}
function setDescription(description) {
document.getElementById("viewerContent").innerHTML = description;
}
function setContent(content) {
document.getElementById("viewerContent").innerHTML = content;
}
</script>
</body>
</html>

View File

@ -1,150 +0,0 @@
package telegra.ph
import com.afollestad.bridge.Bridge
import com.afollestad.bridge.MultipartForm
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
object Api {
private val ApiBase = "https://api.telegra.ph/"
fun getPage(path: String?, accessToken: String?, callback: (success: Boolean, page: Page?) -> Unit) {
Bridge.get("${ApiBase}getPage/$path?access_token=%s&return_content=true", accessToken).asJsonObject { _, jsonObject, exception ->
if (jsonObject != null && exception == null) try {
callback(true, jsonObject.parsePageResponse())
} catch (e: Exception) {
callback(false, null)
}
else callback(false, null)
}
}
fun createPage(accessToken: String?, content: String?, title: String?, name: String?, callback: (success: Boolean, Page?) -> Unit) {
Bridge.get("${ApiBase}createPage?access_token=%s&title=%s&author_name=%s&content=%s&return_content=true", accessToken, title, name, content).asJsonObject { _, jsonObject, exception ->
if (jsonObject != null && exception == null) try {
callback(true, jsonObject.parsePageResponse())
} catch (e: Exception) {
callback(false, null)
}
else callback(false, null)
}
}
fun editPage(accessToken: String?, path: String?, content: String?, title: String?, name: String?, callback: (success: Boolean, Page?) -> Unit) {
Bridge.get("${ApiBase}editPage/$path?access_token=%s&title=%s&author_name=%s&content=%s&return_content=true", accessToken, title, name, content).asJsonObject { _, jsonObject, exception ->
if (jsonObject != null && exception == null) try {
callback(true, jsonObject.parsePageResponse())
} catch (e: Exception) {
callback(false, null)
}
else callback(false, null)
}
}
fun createAccount(callback: (accessToken: String?) -> Unit) {
Bridge.get("${ApiBase}createAccount?short_name=teleposter").asJsonObject { _, jsonObject, exception ->
if (jsonObject != null && exception == null) try {
callback(jsonObject.optJSONObject("result")?.optString("access_token"))
} catch (e: Exception) {
callback(null)
}
else callback(null)
}
}
fun getPageList(accessToken: String?, offset: Int = 0, callback: (success: Boolean, MutableList<Page>?) -> Unit) {
Bridge.get("${ApiBase}getPageList?access_token=%s&limit=200&offset=$offset", accessToken).asJsonObject { _, jsonObject, exception ->
if (jsonObject != null && exception == null) try {
jsonObject.optJSONObject("result")?.let {
val totalCount = it.optInt("total_count")
var currentCount = 200 + offset
val result = mutableListOf<Page>()
it.optJSONArray("pages")?.let {
for (i in 0 until it.length()) {
val page = it.optJSONObject(i)?.parsePage()
if (page != null) result.add(page)
}
}
if (currentCount < totalCount) {
getPageList(accessToken, currentCount) { success, pages ->
if (success && pages != null) {
result.addAll(pages)
callback(true, result)
}
if (!success) callback(false, null)
}
currentCount += 200
} else callback(true, result)
} ?: callback(false, null)
} catch (e: Exception) {
callback(false, null)
}
else callback(false, null)
}
}
private fun JSONObject.parsePageResponse(): Page? {
if (optBoolean("ok", false)) optJSONObject("result")?.let { return it.parsePage() }
return null
}
private fun JSONObject.parsePage(): Page? {
val result: Page = Page()
result.path = optString("path", "")
result.url = optString("url", "")
result.title = optString("title", "")
result.description = optString("description", "")
result.author_name = optString("author_name", "")
result.author_url = optString("author_url", "")
result.image_url = optString("image_url", "")
optJSONArray("content")?.parseContent(result)
result.views = optInt("views", 0)
result.can_edit = optBoolean("can_edit", false)
return result
}
private fun JSONArray.parseContent(result: Page) {
for (i in 0 until length()) {
optJSONObject(i)?.let {
result.content += "<${it.optString("tag", "")}"
it.optJSONObject("attrs")?.let {
for (key in it.keys()) {
result.content += " $key=\"${it.optString(key, "")}\""
}
}
result.content += ">"
it.optJSONArray("children")?.parseContent(result)
result.content += "</${it.optString("tag", "")}>"
}
if (optJSONObject(i) == null) optString(i)?.let {
result.content += it
}
}
}
fun uploadImage(file: File, callback: (url: String?) -> Unit) {
Bridge.post("http://telegra.ph/upload")
.body(MultipartForm().add("FileUpload", file))
.asJsonArray { _, jsonArray, _ ->
val src = jsonArray?.optJSONObject(0)?.optString("src", null)
callback(src)
}
}
}
class Page(
var path: String = "",
var url: String = "",
var title: String = "",
var description: String = "",
var author_name: String = "",
var author_url: String = "",
var image_url: String = "",
var content: String = "",
var views: Int = 0,
var can_edit: Boolean = false
)

View File

@ -4,54 +4,37 @@ import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.webkit.JavascriptInterface
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import im.delight.android.webview.AdvancedWebView
class Editor : WebView {
class Editor @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AdvancedWebView(context, attrs, defStyleAttr) {
private var getCallback: (json: String?) -> Unit? = {}
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init()
init {
prepare()
}
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
private fun init() {
fun prepare() {
this.settings.javaScriptEnabled = true
this.settings.cacheMode = WebSettings.LOAD_NO_CACHE
this.addJavascriptInterface(MyJavaScriptInterface(), "android")
this.settings.loadWithOverviewMode = true
this.settings.useWideViewPort = true
this.loadDataWithBaseURL("http://telegra.ph", context.assets.open("editor.html").bufferedReader().readText(), "text/html", "utf-8", null)
setMixedContentAllowed(true)
this.loadDataWithBaseURL("https://telegra.ph", context.assets.open("editor.html").bufferedReader().readText(), "text/html", "utf-8", null)
}
private inner class MyJavaScriptInterface {
@JavascriptInterface
@SuppressWarnings("unused")
fun getText(json: String) {
getCallback(json)
}
}
fun setText(html: String) {
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
setText(html)
}
}
this.loadUrl("javascript:$('#summernote').summernote('reset');")
this.loadUrl("javascript:$('#summernote').summernote('code', '" + html.replace("'", "\\'") + "');")
}
fun addImage(url: String) {
this.loadUrl("javascript:$('#summernote').summernote('insertImage', '$url');")
fun setContent(content: String?) {
this.loadUrl("javascript:setContent('${content?.replace("'", "\\'")}');")
}
fun getText(callback: (json: String?) -> Unit) {

View File

@ -2,24 +2,70 @@ package telegra.ph
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.folderselector.FileChooserDialog
import com.afollestad.materialdialogs.input.input
import com.afollestad.materialdialogs.list.listItemsMultiChoice
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import im.delight.android.webview.AdvancedWebView
import pub.devrel.easypermissions.AfterPermissionGranted
import pub.devrel.easypermissions.EasyPermissions
import java.io.File
import java.net.URI
class MainActivity : AppCompatActivity(), AdvancedWebView.Listener, FileChooserDialog.FileCallback {
private val webView: AdvancedWebView? by lazy { findViewById(R.id.webView) as AdvancedWebView? }
private val editor: Editor? by lazy { findViewById(R.id.editor) as Editor? }
class MainActivity : AppCompatActivity() {
private val viewer: Viewer? by lazy {
findViewById<Viewer?>(R.id.viewer)?.apply {
setListener(this@MainActivity, object : AdvancedWebView.Listener {
override fun onPageFinished(url: String?) {
viewerPendingPage?.let { viewer?.showPage(it) }
viewerPendingPage = null
}
private var currentUrl = ""
private var currentPage: Page? = null
override fun onPageError(errorCode: Int, description: String?, failingUrl: String?) {
}
override fun onDownloadRequested(url: String?, suggestedFilename: String?, mimeType: String?, contentLength: Long, contentDisposition: String?, userAgent: String?) {
}
override fun onExternalPageRequest(url: String?) {
AdvancedWebView.Browsers.openUrl(this@MainActivity, url)
}
override fun onPageStarted(url: String?, favicon: Bitmap?) {
}
})
}
}
private var viewerPendingPage: TelegraphApi.Page? = null
private val editor: Editor? by lazy {
findViewById<Editor?>(R.id.editor)?.apply {
setListener(this@MainActivity, object : AdvancedWebView.Listener {
override fun onPageFinished(url: String?) {
editorPendingPage?.let { editor?.setContent(it.content) }
editorPendingPage = null
}
override fun onPageError(errorCode: Int, description: String?, failingUrl: String?) {
}
override fun onDownloadRequested(url: String?, suggestedFilename: String?, mimeType: String?, contentLength: Long, contentDisposition: String?, userAgent: String?) {
}
override fun onExternalPageRequest(url: String?) {
AdvancedWebView.Browsers.openUrl(this@MainActivity, url)
}
override fun onPageStarted(url: String?, favicon: Bitmap?) {
}
})
}
}
private var editorPendingPage: TelegraphApi.Page? = null
private var currentPage: TelegraphApi.Page? = null
private var editorMode = true
private var canEdit = false
private var isEdit = false
@ -27,152 +73,104 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener, FileChooserD
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView?.apply {
setListener(this@MainActivity, this@MainActivity)
setMixedContentAllowed(true)
setCookiesEnabled(true)
setThirdPartyCookiesEnabled(true)
addPermittedHostname("telegra.ph")
isHorizontalScrollBarEnabled = false
isVerticalScrollBarEnabled = false
overScrollMode = View.OVER_SCROLL_NEVER
if (accessToken.isBlank()) TelegraphApi.createAccount(shortName = "teleposter") { success, account, error ->
if (success && account != null && account.accessToken != null) {
accessToken = account.accessToken
} else {
showError(error)
}
}
if (accessToken().isNullOrBlank()) Api.createAccount { accessToken ->
if (accessToken != null) saveAccessToken(accessToken)
if (intent.action == Intent.ACTION_VIEW) {
val uri = URI.create(intent.dataString)
when (uri.host) {
"telegra.ph", "graph.org" -> loadPage(uri.path)
"edit.telegra.ph", "edit.graph.org" -> login(uri.toString())
}
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.action == Intent.ACTION_VIEW) {
val uri = URI.create(intent.dataString)
when (uri.host) {
"telegra.ph", "graph.org" -> loadPage(uri.path)
"edit.telegra.ph", "edit.graph.org" -> login(uri.toString())
}
}
if (intent.action == Intent.ACTION_VIEW && intent.dataString.contains("telegra.ph")) loadPage(intent.dataString.split("/").last())
else loadEditor()
}
private fun loadEditor(path: String? = null) {
runOnUiThread {
editorMode = true
canEdit = false
isEdit = false
invalidateOptionsMenu()
editor?.visibility = View.VISIBLE
webView?.visibility = View.GONE
currentPage = null
// Load
if (path != null) Api.getPage(path, accessToken()) { success, page ->
if (success) runOnUiThread {
isEdit = true
currentPage = page
editor?.setText(page?.content ?: "")
}
else showError()
editorMode = true
canEdit = false
isEdit = false
invalidateOptionsMenu()
editor?.visibility = View.VISIBLE
viewer?.visibility = View.GONE
currentPage = null
// Load
if (path != null) TelegraphApi.getPage(accessToken, path, true) { success, page, error ->
if (success && page != null) {
isEdit = true
currentPage = page
editorPendingPage = page
editor?.prepare()
} else {
showError(error)
}
} else {
editor?.prepare()
}
}
private fun login(authUrl: String) {
TelegraphApi.login(authUrl) { success, accessToken, account ->
if (success && !accessToken.isNullOrEmpty()) {
this.accessToken = accessToken
this.authorName = account?.authorName
showMessage(getString(R.string.success), getString(R.string.login_success))
} else showError(getString(R.string.login_failed))
}
}
private fun loadPage(path: String) {
runOnUiThread {
editorMode = false
canEdit = false
invalidateOptionsMenu()
webView?.visibility = View.VISIBLE
editor?.visibility = View.GONE
currentPage = null
// Load
Api.getPage(path, accessToken()) { success, page ->
if (success) showPage(page)
else showError()
editorMode = false
canEdit = false
invalidateOptionsMenu()
viewer?.visibility = View.VISIBLE
editor?.visibility = View.GONE
currentPage = null
// Load
TelegraphApi.getPage(accessToken, path, true) { success, page, error ->
if (success && page != null) {
canEdit = page.canEdit ?: false
invalidateOptionsMenu()
currentPage = page
viewerPendingPage = page
viewer?.prepare()
}
else showError(error)
}
}
private fun showPage(page: Page?) {
runOnUiThread {
editorMode = false
canEdit = page?.can_edit ?: false
invalidateOptionsMenu()
webView?.visibility = View.VISIBLE
editor?.visibility = View.GONE
currentPage = page
webView?.clearHistory()
// Show
page?.let {
var html = getString(R.string.viewer_html_head)
html += "<h1>${it.title}</h1>"
if (!it.author_name.isNullOrEmpty() && !it.author_url.isNullOrBlank()) html += "<a href=\"${it.author_url}\">${it.author_name}</a><br>"
else if (!it.author_name.isNullOrEmpty()) html += "${it.author_name}<br>"
if (it.views != 0) html += "${it.views} times viewed<br><br>"
if (it.content.isNullOrBlank()) html += it.description.replace("\n", "<br>") else html += it.content
html += getString(R.string.viewer_html_end)
webView?.loadDataWithBaseURL(it.url, html, "text/html; charset=UTF-8", null, null)
currentUrl = it.url
}
}
}
private fun showError(message: String? = null) = showMessage(getString(R.string.error), message
?: getString(R.string.error_desc))
private fun showError() {
runOnUiThread {
MaterialDialog.Builder(this)
.title(R.string.error)
.content(R.string.error_desc)
.positiveText(android.R.string.ok)
.show()
}
}
@AfterPermissionGranted(100)
private fun uploadImage() {
if (EasyPermissions.hasPermissions(this, "android.permission.READ_EXTERNAL_STORAGE")) {
FileChooserDialog.Builder(this)
.mimeType("image/*")
.show()
} else {
EasyPermissions.requestPermissions(this, "", 100, "android.permission.READ_EXTERNAL_STORAGE")
}
}
override fun onPageFinished(url: String?) {
}
override fun onPageStarted(url: String?, favicon: Bitmap?) {
}
override fun onPageError(errorCode: Int, description: String?, failingUrl: String?) {
}
override fun onDownloadRequested(url: String?, suggestedFilename: String?, mimeType: String?, contentLength: Long, contentDisposition: String?, userAgent: String?) {
}
override fun onExternalPageRequest(url: String?) {
AdvancedWebView.Browsers.openUrl(this, url)
}
override fun onFileSelection(p0: FileChooserDialog, file: File) {
Api.uploadImage(file) { src ->
if (src != null) editor?.addImage(src)
}
}
override fun onFileChooserDismissed(p0: FileChooserDialog) {
}
override fun onResume() {
super.onResume()
webView?.onResume()
}
override fun onPause() {
webView?.onPause()
super.onPause()
}
override fun onDestroy() {
webView?.onDestroy()
super.onDestroy()
private fun showMessage(title: String? = null, message: String? = null) {
MaterialDialog(this@MainActivity)
.title(text = title ?: "")
.message(text = message ?: "")
.positiveButton(android.R.string.ok)
.show()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
webView?.onActivityResult(requestCode, resultCode, data)
editor?.onActivityResult(requestCode, resultCode, data)
}
override fun onBackPressed() {
if (webView?.onBackPressed() == false) return
if (viewer?.onBackPressed() == false) return
else super.onBackPressed()
}
@ -195,34 +193,35 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener, FileChooserD
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.image -> {
uploadImage()
true
}
R.id.create -> {
loadEditor()
true
}
R.id.publish -> {
editor?.getText { json ->
MaterialDialog.Builder(this)
MaterialDialog(this@MainActivity)
.title(R.string.title_question)
.input(getString(R.string.title_hint), currentPage?.title ?: "", { _, title ->
MaterialDialog.Builder(this)
.input(hintRes = R.string.title_hint, prefill = currentPage?.title
?: "", allowEmpty = false) { _, title ->
MaterialDialog(this@MainActivity)
.title(R.string.name_question)
.input(getString(R.string.name_hint), if (isEdit) currentPage?.author_name ?: authorName() ?: "" else authorName() ?: "", { _, name ->
if (!isEdit) saveAuthorName(name.toString())
if (isEdit) Api.editPage(accessToken(), currentPage?.path, json, title.toString(), name.toString()) { success, page ->
if (success) showPage(page)
else showError()
.input(hintRes = R.string.name_hint, prefill = if (isEdit) currentPage?.authorName
?: authorName ?: "" else authorName
?: "", allowEmpty = true) { _, name ->
if (!isEdit) authorName = name.toString()
if (isEdit) TelegraphApi.editPage(accessToken, currentPage?.path
?: "", authorName = name.toString(), title = title.toString(), content = json
?: "", returnContent = true) { success, page, error ->
if (success && page != null) loadPage(page.path)
else showError(error)
} else TelegraphApi.createPage(accessToken, content = json
?: "", title = title.toString(), authorName = name.toString(), returnContent = true) { success, page, error ->
if (success && page != null) loadPage(page.path)
else showError(error)
}
else Api.createPage(accessToken(), json, title.toString(), name.toString()) { success, page ->
if (success) showPage(page)
else showError()
}
})
}
.show()
})
}
.show()
}
true
@ -232,50 +231,59 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener, FileChooserD
true
}
R.id.bookmarks -> {
MaterialDialog.Builder(this)
MaterialDialog(this@MainActivity)
.title(R.string.bookmarks)
.positiveText(android.R.string.ok)
.items(bookmarks().reversed().map { it.split("xxx;xxx")[1] })
.itemsCallback { _, _, i, _ ->
loadPage(bookmarks().reversed().map { it.split("xxx;xxx")[0] }[i])
.positiveButton(R.string.open)
.negativeButton(android.R.string.cancel)
.listItemsSingleChoice(items = bookmarks().reversed().map { it.second }) { _, index, _ ->
loadPage(bookmarks().reversed().map { it.first }[index])
}
.itemsLongCallback { _, _, i, _ ->
MaterialDialog.Builder(this)
.show()
true
}
R.id.delete_bookmark -> {
MaterialDialog(this@MainActivity)
.title(R.string.delete_bookmark)
.positiveButton(R.string.delete)
.negativeButton(android.R.string.cancel)
.listItemsMultiChoice(items = bookmarks().reversed().map { it.second }) { _, indices, _ ->
MaterialDialog(this@MainActivity)
.title(R.string.delete)
.content(R.string.delete_question)
.positiveText(android.R.string.yes)
.negativeText(android.R.string.no)
.onPositive { _, _ ->
deleteBookmark(bookmarks().reversed().map { it.split("xxx;xxx")[0] }[i])
.message(R.string.delete_question)
.positiveButton(R.string.yes)
.negativeButton(R.string.no)
.positiveButton {
val tmpBookmarks = bookmarks().reversed().map { it.first }
for (index in indices) deleteBookmark(tmpBookmarks[index])
}
.show()
true
}
.show()
true
}
R.id.published -> {
Api.getPageList(accessToken()) { success, result ->
if (!success || result == null || result.isEmpty()) showError()
else {
MaterialDialog.Builder(this)
TelegraphApi.getPageList(accessToken) { success, pageList, error ->
if (success && pageList != null && pageList.pages != null) {
MaterialDialog(this@MainActivity)
.title(R.string.published)
.positiveText(android.R.string.ok)
.items(result.map(Page::title))
.itemsCallback { _, _, i, _ ->
loadPage(result.map(Page::path)[i])
.positiveButton(R.string.open)
.negativeButton(android.R.string.cancel)
.listItemsSingleChoice(items = pageList.pages.map { it.title }) { _, i, _ ->
loadPage(pageList.pages[i].path)
}
.show()
}
} else showError(error)
}
true
}
R.id.bookmark -> {
MaterialDialog.Builder(this)
MaterialDialog(this@MainActivity)
.title(R.string.title_question)
.input(getString(R.string.title_hint), "", { _, input ->
addBookmark("${currentUrl.split("/").last()}xxx;xxx$input")
})
.input(hintRes = R.string.title_hint, prefill = currentPage?.title
?: "", allowEmpty = false) { _, input ->
val curPage = currentPage
if (curPage?.url != null) addBookmark(curPage.url.split("/").last(), input.toString())
}
.show()
true
}
@ -283,25 +291,28 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener, FileChooserD
val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.type = "text/plain"
shareIntent.putExtra(Intent.EXTRA_TITLE, webView?.title)
shareIntent.putExtra(Intent.EXTRA_TEXT, currentUrl)
shareIntent.putExtra(Intent.EXTRA_TITLE, currentPage?.title)
shareIntent.putExtra(Intent.EXTRA_TEXT, currentPage?.url)
startActivity(Intent.createChooser(shareIntent, getString(R.string.share)))
true
}
R.id.help -> {
MaterialDialog.Builder(this)
.title(R.string.help)
.content(R.string.help_text, true)
.positiveText(android.R.string.ok)
R.id.about -> {
val aboutIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/jlelse/teleposter"))
startActivity(aboutIntent)
true
}
R.id.login -> {
MaterialDialog(this@MainActivity)
.title(R.string.login)
.message(R.string.login_desc)
.positiveButton(android.R.string.ok)
.positiveButton {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/telegraph")))
}
.show()
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
}

View File

@ -1,28 +1,42 @@
package telegra.ph
import android.content.Context
import android.preference.PreferenceManager
import androidx.preference.PreferenceManager
fun Context.bookmarks(): MutableList<String> = PreferenceManager.getDefaultSharedPreferences(this).getString("bookmarks", null)?.split("+++;+++")?.toMutableList() ?: mutableListOf("apixxx;xxxAPI Documentation")
const val listItemSeparator = "+++;+++"
const val itemSeparator = "xxx;xxx"
fun Context.addBookmark(entry: String) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("bookmarks", bookmarks().apply {
add(entry)
}.joinToString(separator = "+++;+++")).apply()
fun Context.bookmarks(): List<Pair<String, String>> {
val list = PreferenceManager.getDefaultSharedPreferences(this).getString("bookmarks", null)?.split(listItemSeparator)?.map {
val splitParts = it.split(itemSeparator)
if (splitParts.size == 2) splitParts[0] to splitParts[1]
else null
}?.filterNotNull()
return if (list != null && list.isNotEmpty()) list else listOf("api" to "API Documentation")
}
fun Context.addBookmark(path: String, title: String) {
saveBookmarks(bookmarks().plus(path to title))
}
fun Context.deleteBookmark(path: String) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("bookmarks", bookmarks().filter { !it.contains(path) }.joinToString(separator = "+++;+++")).apply()
saveBookmarks(bookmarks().filter { it.first != path })
}
fun Context.accessToken(): String? = PreferenceManager.getDefaultSharedPreferences(this).getString("accessToken", null)
fun Context.saveAccessToken(token: String) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("accessToken", token).apply()
fun Context.saveBookmarks(bookmarks: List<Pair<String, String>>) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("bookmarks",
bookmarks.joinToString(separator = listItemSeparator) { "${it.first}$itemSeparator${it.second}" }
).apply()
}
fun Context.authorName(): String? = PreferenceManager.getDefaultSharedPreferences(this).getString("authorName", null)
var Context.accessToken: String
get() = PreferenceManager.getDefaultSharedPreferences(this).getString("accessToken", "") as String
set(value) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("accessToken", value).apply()
}
fun Context.saveAuthorName(name: String) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("authorName", name).apply()
}
var Context.authorName: String?
get() = PreferenceManager.getDefaultSharedPreferences(this).getString("authorName", null)
set(value) {
PreferenceManager.getDefaultSharedPreferences(this).edit().putString("authorName", value).apply()
}

View File

@ -0,0 +1,191 @@
package telegra.ph
import com.github.kittinunf.fuel.core.FuelError
import com.github.kittinunf.fuel.core.FuelManager
import com.github.kittinunf.fuel.core.Request
import com.github.kittinunf.fuel.core.Response
import com.github.kittinunf.fuel.core.interceptors.redirectResponseInterceptor
import com.github.kittinunf.fuel.httpPost
import com.github.kittinunf.fuel.json.FuelJson
import com.github.kittinunf.fuel.json.responseJson
import com.github.kittinunf.result.Result
import org.json.JSONArray
import org.json.JSONObject
import java.net.HttpCookie
object TelegraphApi {
private var loginAccessToken: String? = null
init {
FuelManager.instance.basePath = "https://api.telegra.ph"
FuelManager.instance.removeAllResponseInterceptors()
FuelManager.instance.addResponseInterceptor {
telegraphLoginInterceptor()
}
// Fix login
FuelManager.instance.addResponseInterceptor {
redirectResponseInterceptor(FuelManager.instance)
it
}
}
private fun callService(method: String, parameters: List<Pair<String, Any?>>, handler: (Request, Response, Result<FuelJson, FuelError>) -> Unit) {
val requestObject = JSONObject()
parameters.forEach {
requestObject.put(it.first, it.second)
}
method.httpPost().header(mapOf("Content-Type" to "application/json")).body(requestObject.toString()).responseJson(handler)
}
fun createAccount(shortName: String, authorName: String? = null, authorUrl: String? = null, callback: (success: Boolean, account: Account?, error: String?) -> Unit) {
callService("/createAccount", listOf("short_name" to shortName, "author_name" to authorName, "author_url" to authorUrl)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Account(obj), null) }
}
}
fun editAccountInfo(accessToken: String, shortName: String? = null, authorName: String? = null, authorUrl: String? = null, callback: (success: Boolean, account: Account?, error: String?) -> Unit) {
callService("/editAccountInfo", listOf("access_token" to accessToken, "short_name" to shortName, "author_name" to authorName, "author_url" to authorUrl)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Account(obj), null) }
}
}
fun getAccountInfo(accessToken: String, fields: Array<String>? = null, callback: (success: Boolean, account: Account?, error: String?) -> Unit) {
callService("/getAccountInfo", listOf("access_token" to accessToken, "fields" to fields)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Account(obj), null) }
}
}
fun revokeAccessToken(accessToken: String, callback: (success: Boolean, account: Account?, error: String?) -> Unit) {
callService("/revokeAccessToken", listOf("access_token" to accessToken)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Account(obj), null) }
}
}
fun createPage(accessToken: String, title: String, authorName: String? = null, authorUrl: String? = null, content: String, returnContent: Boolean? = null, callback: (success: Boolean, page: Page?, error: String?) -> Unit) {
callService("/createPage", listOf("access_token" to accessToken, "title" to title, "author_name" to authorName, "author_url" to authorUrl, "content" to content, "return_content" to returnContent)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Page(obj), null) }
}
}
fun editPage(accessToken: String, path: String, title: String, authorName: String? = null, authorUrl: String? = null, content: String, returnContent: Boolean? = null, callback: (success: Boolean, page: Page?, error: String?) -> Unit) {
callService("/editPage", listOf("access_token" to accessToken, "path" to path, "title" to title, "author_name" to authorName, "author_url" to authorUrl, "content" to content, "return_content" to returnContent)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Page(obj), null) }
}
}
fun getPage(accessToken: String? = null, path: String, returnContent: Boolean? = null, callback: (success: Boolean, page: Page?, error: String?) -> Unit) {
callService("/getPage", listOf("access_token" to accessToken, "path" to path, "return_content" to returnContent)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, Page(obj), null) }
}
}
fun getPageList(accessToken: String, offset: Int? = null, limit: Int? = null, callback: (success: Boolean, page: PageList?, error: String?) -> Unit) {
callService("/getPageList", listOf("access_token" to accessToken, "offset" to offset, "limit" to limit)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, PageList(obj), null) }
}
}
fun getViews(path: String, year: Int? = null, month: Int? = null, day: Int? = null, hour: Int? = null, callback: (success: Boolean, page: PageViews?, error: String?) -> Unit) {
callService("/getViews", listOf("path" to path, "year" to year, "month" to month, "day" to day, "hour" to hour)) { _, _, result ->
handleResponse(result, callback) { obj: JSONObject -> callback(true, PageViews(obj), null) }
}
}
class Account(json: JSONObject) {
val shortName: String? = json.optString("short_name")
val authorName: String? = json.optString("author_name")
val authorUrl: String? = json.optString("author_url")
val accessToken: String? = json.optString("access_token")
val authUrl: String? = json.optString("auth_url")
val pageCount: Int? = json.optInt("page_count")
}
class PageList(json: JSONObject) {
val totalCount: Int? = json.optInt("total_count")
val pages: Array<Page>? = json.optJSONArray("pages")?.let {
mutableListOf<Page>().apply { for (i in 0 until it.length()) add(Page(it.optJSONObject(i))) }.toTypedArray()
}
}
class Page(json: JSONObject) {
val path: String = json.optString("path")
val url: String = json.optString("url")
val title: String = json.optString("title")
val description: String = json.optString("description")
val authorName: String? = json.optString("author_name")
val authorUrl: String? = json.optString("author_url")
val imageUrl: String? = json.optString("image_url")
val content: String? = json.optJSONArray("content")?.parseContent()
val views: Int = json.optInt("views")
val canEdit: Boolean? = json.optBoolean("can_edit")
private fun JSONArray.parseContent(): String? {
var content = ""
for (i in 0 until length()) {
optJSONObject(i)?.let {
content += "<${it.optString("tag", "")}"
it.optJSONObject("attrs")?.let {
for (key in it.keys()) {
content += " $key=\"${it.optString(key, "")}\""
}
}
content += ">"
content += it.optJSONArray("children")?.parseContent() ?: ""
content += "</${it.optString("tag", "")}>"
}
if (optJSONObject(i) == null) optString(i)?.let {
content += it
}
// Fix mixed content
content = content.replace("http://telegra.ph", "https://telegra.ph")
content = content.replace("http://graph.org", "https://graph.org")
}
return content
}
}
class PageViews(json: JSONObject) {
val views: Int? = json.optInt("views")
}
// Teleposter
private fun <T> handleResponse(result: Result<FuelJson, FuelError>, handler: (success: Boolean, obj: T?, error: String?) -> Unit, callback: (obj: JSONObject) -> Unit) {
val (json, error) = result
if (error == null && json != null) {
val jsonObj = json.obj()
if (jsonObj.optBoolean("ok")) {
callback(jsonObj.optJSONObject("result"))
} else {
handler(false, null, jsonObj.optString("error"))
}
} else {
handler(false, null, error?.message)
}
}
// Dirty hacks
fun login(authUrl: String, callback: (success: Boolean, accessToken: String?, account: Account?) -> Unit) {
loginAccessToken = null
authUrl.httpPost().response { _, _, _ ->
if (loginAccessToken != null) getAccountInfo(accessToken = loginAccessToken!!) { success, account, _ ->
if (success) callback(true, loginAccessToken, account)
else callback(false, null, null)
} else callback(false, null, null)
}
}
private fun telegraphLoginInterceptor(): (Request, Response) -> Response =
{ _, response ->
response.headers["Set-Cookie"]
.flatMap { HttpCookie.parse(it) }
.find { it.name == "tph_token" }
?.let {
loginAccessToken = it.value
}
response
}
}

View File

@ -0,0 +1,57 @@
package telegra.ph
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.webkit.JavascriptInterface
import im.delight.android.webview.AdvancedWebView
class Viewer @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AdvancedWebView(context, attrs, defStyleAttr) {
init {
prepare()
}
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
fun prepare() {
settings.javaScriptEnabled = true
settings.loadWithOverviewMode = true
settings.useWideViewPort = true
overScrollMode = View.OVER_SCROLL_NEVER
setMixedContentAllowed(true)
loadDataWithBaseURL("https://telegra.ph", context.assets.open("viewer.html").bufferedReader().readText(), "text/html", "utf-8", null)
}
fun showPage(page: TelegraphApi.Page) {
clearHistory()
setArticleTitle(page.title)
setAuthor(page.authorName, page.authorUrl)
setViews(page.views)
if (page.content == null) setDescription(page.description)
else setContent(page.content)
}
private fun setArticleTitle(title: String) {
this.loadUrl("javascript:setTitle('$title');")
}
private fun setAuthor(author: String?, url: String?) {
this.loadUrl("javascript:setAuthor('$author','$url');")
}
private fun setViews(views: Int) {
this.loadUrl("javascript:setViews('$views');")
}
private fun setDescription(description: String) {
this.loadUrl("javascript:setDescription('${description.replace("\n", "<br>")}');")
}
private fun setContent(content: String?) {
this.loadUrl("javascript:setContent('${content?.replace("'", "\\'")}');")
}
}

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -8,14 +7,10 @@
<telegra.ph.Editor
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
<im.delight.android.webview.AdvancedWebView
android:id="@+id/webView"
<telegra.ph.Viewer
android:id="@+id/viewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"/>
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -1,44 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/image"
android:title="@string/add_image"
app:showAsAction="ifRoom" />
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/create"
android:title="@string/create"
app:showAsAction="ifRoom"/>
app:showAsAction="ifRoom" />
<item
android:id="@+id/publish"
android:title="@string/publish"
app:showAsAction="ifRoom"/>
app:showAsAction="ifRoom" />
<item
android:id="@+id/edit"
android:title="@string/edit"
app:showAsAction="ifRoom"/>
app:showAsAction="ifRoom" />
<item
android:id="@+id/try_edit"
android:title="@string/try_edit"
app:showAsAction="ifRoom"/>
app:showAsAction="ifRoom" />
<item
android:id="@+id/bookmark"
android:title="@string/bookmark_this"
app:showAsAction="ifRoom"/>
app:showAsAction="never" />
<item
android:id="@+id/bookmarks"
android:title="@string/bookmarks"
app:showAsAction="ifRoom"/>
app:showAsAction="never" />
<item
android:id="@+id/delete_bookmark"
android:title="@string/delete_bookmark"
app:showAsAction="never" />
<item
android:id="@+id/published"
android:title="@string/published"
app:showAsAction="ifRoom"/>
app:showAsAction="never" />
<item
android:id="@+id/share"
android:title="@string/share"
app:showAsAction="ifRoom"/>
app:showAsAction="never" />
<item
android:id="@+id/help"
android:title="@string/help"
app:showAsAction="ifRoom"/>
android:id="@+id/login"
android:title="@string/login"
app:showAsAction="never" />
<item
android:id="@+id/about"
android:title="@string/about"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,29 @@
<resources>
<string name="app_name">Teleposter</string>
<string name="yes">Ja</string>
<string name="no">Nein</string>
<string name="share">Teilen</string>
<string name="about">Über</string>
<string name="bookmarks">Lesezeichen</string>
<string name="bookmark_this">Als Lesezeichen setzen</string>
<string name="delete_bookmark">Lesezeichen löschen</string>
<string name="create">Neu</string>
<string name="title_question">Titel?</string>
<string name="title_hint">Fantastischer Post #1</string>
<string name="delete">Löschen</string>
<string name="delete_question">Wirklich löschen?</string>
<string name="publish">Veröffentlichen</string>
<string name="edit">Bearbeiten</string>
<string name="try_edit">Versuchen zu bearbeiten</string>
<string name="error">Fehler</string>
<string name="error_desc">Etwas unerwartetes ist passiert!</string>
<string name="published">Veröffentlichte Posts</string>
<string name="name_question">Dein Name?</string>
<string name="name_hint">Fantastischer Autor</string>
<string name="login_failed">Login fehlgeschlagen. Noch einmal versuchen!</string>
<string name="success">Erfolg</string>
<string name="login_success">Erfolgreich eingeloggt!</string>
<string name="login">Login</string>
<string name="login_desc">Öffne den Telegraph Bot in Telegram, wähle \"Login as * on this device\" und wähle Teleposter.</string>
<string name="open">Öffnen</string>
</resources>

View File

@ -0,0 +1,29 @@
<resources>
<string name="app_name">Teleposter</string>
<string name="yes"></string>
<string name="no">No</string>
<string name="share">Compartir</string>
<string name="about">Acerca de</string>
<string name="bookmarks">Favoritos</string>
<string name="bookmark_this">Añadir a favoritos</string>
<string name="delete_bookmark">Borrar favorito</string>
<string name="create">Nueva</string>
<string name="title_question">¿Título?</string>
<string name="title_hint">Entrada increíble #1</string>
<string name="delete">Borrar</string>
<string name="delete_question">¿Realmente quiere eliminar esto?</string>
<string name="publish">Publicar</string>
<string name="edit">Editar</string>
<string name="try_edit">Prueba a editar.</string>
<string name="error">Error</string>
<string name="error_desc">¡Pasó algo inesperado!</string>
<string name="published">Entradas publicadas</string>
<string name="name_question">¿Su nombre?</string>
<string name="name_hint">Awesome Writer</string>
<string name="login_failed">Login fallido. ¡Inténtelo de nuevo!</string>
<string name="success">Hecho</string>
<string name="login_success">¡Login realizado correctamente!</string>
<string name="login">Login</string>
<string name="login_desc">Abre el bot de Telegraph en Telegram, elija \"Login as * on this device\" y seleccione Teleposter.</string>
<string name="open">Abrir</string>
</resources>

View File

@ -0,0 +1,29 @@
<resources>
<string name="app_name">Teleposter</string>
<string name="yes">да</string>
<string name="no">Нет</string>
<string name="share">Поделиться</string>
<string name="about">Информация</string>
<string name="bookmarks">Закладки</string>
<string name="bookmark_this">Добавить в закладки</string>
<string name="delete_bookmark">Удалить закладку</string>
<string name="create">Новый</string>
<string name="title_question">Заглавие?</string>
<string name="title_hint">Крутой пост #1</string>
<string name="delete">Удалить</string>
<string name="delete_question">Вы действительно хотите удалить это?</string>
<string name="publish">Публикация</string>
<string name="edit">Изменить</string>
<string name="try_edit">Повторить изменение</string>
<string name="error">Ошибка</string>
<string name="error_desc">Случилось что-то неожиданное!</string>
<string name="published">Опубликованные посты</string>
<string name="name_question">Ваше имя?</string>
<string name="name_hint">Крутой писатель</string>
<string name="login_failed">Неудачный вход. Попробуй еще раз!</string>
<string name="success">Готово</string>
<string name="login_success">Вы успешно вошли!</string>
<string name="login">Вход</string>
<string name="login_desc">Откройте Telegraph bot в Telegram, выберите \"Login as * on this device\" и выберите Teleposter.</string>
<string name="open">Открыть</string>
</resources>

View File

@ -0,0 +1,29 @@
<resources>
<string name="app_name">Teleposter</string>
<string name="yes">Evet</string>
<string name="no">Hayır</string>
<string name="share">Paylaş</string>
<string name="about">Hakkında</string>
<string name="bookmarks">Yer imleri</string>
<string name="bookmark_this">Buna yer işareti koy</string>
<string name="delete_bookmark">Yer işaretini sil</string>
<string name="create">Yeni</string>
<string name="title_question">Başlık?</string>
<string name="title_hint">Harika mesaj #1</string>
<string name="delete">Sil</string>
<string name="delete_question">Bunu gerçekten silmek istiyor musun?</string>
<string name="publish">Yayınla</string>
<string name="edit">Düzenle</string>
<string name="try_edit">Düzenlemeye çalış</string>
<string name="error">Hata</string>
<string name="error_desc">Beklenmedik bir şey oldu!</string>
<string name="published">Mesaj yayınlandı</string>
<string name="name_question">Adınız?</string>
<string name="name_hint">Süper yazar</string>
<string name="login_failed">Giriş başarısız. Tekrar deneyin!</string>
<string name="success">Başarılı</string>
<string name="login_success">Başarıyla giriş yaptınız!</string>
<string name="login">Giriş</string>
<string name="login_desc">Telegram\'da Telegraph botunu açın, \"Login as * on this device\" seçeneğini ve Teleposter\'ı seçin.</string>
<string name="open"></string>
</resources>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
<color name="primaryColor">#fafafa</color>
<color name="primaryDarkColor">#c7c7c7</color>
<color name="primaryTextColor">#000000</color>
</resources>

View File

@ -1,29 +1,29 @@
<resources>
<string name="app_name">Teleposter</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="share">Share</string>
<string name="help">Help</string>
<string name="help_text">
<![CDATA[<h3>Why can\'t I select headings?</h3>Unfortunately I wasn\'t able to add the feature yet, but you can do it manually via the code editor.<h3>Why can\'t I login with my Telegram account?</h3>That\'s not possible!<h3>Used libraries</h3><a href=\"https://github.com/afollestad/material-dialogs\">Material Dialogs</a>, <a href=\"https://github.com/afollestad/bridge\">Bridge</a>, <a href=\"https://github.com/delight-im/Android-AdvancedWebView\">AdvancedWebView</a>, <a href=\"https://github.com/googlesamples/easypermissions\">EasyPermissions</a><h3>About</h3>This app is made by <a href="https://jlelse.eu">Jan-Lukas Else</a> and it\'s code is published on <a href="https://github.com/jlelse/teleposter">Github</a>.]]></string>
<string name="about">About</string>
<string name="bookmarks">Bookmarks</string>
<string name="bookmark_this">Bookmark this</string>
<string name="create">Create</string>
<string name="delete_bookmark">Delete Bookmark</string>
<string name="create">New</string>
<string name="title_question">Title?</string>
<string name="title_hint">Awesome Post #1</string>
<string name="delete">Delete</string>
<string name="delete_question">Do you really want to delete this?</string>
<string name="publish">Publish</string>
<string name="edit">Edit (safe)</string>
<string name="try_edit">Edit (unsafe)</string>
<string name="edit">Edit</string>
<string name="try_edit">Try to edit</string>
<string name="error">Error</string>
<string name="error_desc">Something unexpected happened!</string>
<string name="viewer_html_head">
<![CDATA[<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\"><style> * { max-width: 100%; height: auto; word-break: break-all; word-break: break-word; }</style></head><body>]]>
</string>
<string name="viewer_html_end">
<![CDATA[<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js\"></script><script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\"></script></body></html>]]>
</string>
<string name="published">Published posts</string>
<string name="name_question">Your name?</string>
<string name="name_hint">Awesome Writer</string>
<string name="add_image">Add image</string>
<string name="login_failed">Login failed. Try again!</string>
<string name="success">Success</string>
<string name="login_success">You successfully logged in!</string>
<string name="login">Login</string>
<string name="login_desc">Open the Telegraph bot in Telegram, select \"Login as * on this device\" and choose Teleposter.</string>
<string name="open">Open</string>
</resources>

View File

@ -1,10 +1,10 @@
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:colorBackground">@android:color/white</item>
</style>
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryDark">@color/primaryDarkColor</item>
<item name="android:textColorPrimary">@color/primaryTextColor</item>
<item name="md_color_button_text">@color/primaryTextColor</item>
</style>
</resources>

View File

@ -1,25 +1,25 @@
buildscript {
ext.kotlin_version = '1.1.3-2'
ext.kotlin_version = '1.4.10'
repositories {
jcenter()
mavenCentral()
maven { url "https://maven.google.com" }
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id 'com.github.ben-manes.versions' version '0.15.0'
id 'com.github.ben-manes.versions' version '0.33.0'
}
allprojects {
repositories {
jcenter()
mavenCentral()
google()
maven { url "https://jitpack.io" }
maven { url "https://maven.google.com" }
}
}

View File

@ -1,4 +1,6 @@
kotlin.incremental=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx3072M
org.gradle.jvmargs=-Xmx3072M
android.enableD8=true
android.useAndroidX=true
android.enableJetifier=true

Binary file not shown.

View File

@ -1,6 +1,5 @@
#Fri Jul 14 20:42:12 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.1-bin.zip

59
gradlew vendored
View File

@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# 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
#
# https://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.
#
##############################################################################
##
## Gradle start up script for UN*X
@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
@ -66,6 +82,7 @@ esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
@ -109,10 +126,11 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
@ -138,35 +156,30 @@ if $cygwin ; then
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
i=`expr $i + 1`
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

173
gradlew.bat vendored Normal file → Executable file
View File

@ -1,84 +1,89 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega