From ad5be8ed47cd484c20bc1ae3765432e622e922ae Mon Sep 17 00:00:00 2001 From: Jan-Lk Else Date: Sun, 16 Jul 2017 08:04:35 +0200 Subject: [PATCH] Added option to add images; improved code --- README.md | 2 +- app/build.gradle | 6 +- app/src/main/AndroidManifest.xml | 3 +- app/src/main/java/telegra/ph/Api.kt | 44 +++++++++----- app/src/main/java/telegra/ph/Editor.kt | 13 +++-- app/src/main/java/telegra/ph/MainActivity.kt | 61 +++++++++++++++----- app/src/main/res/menu/activity_main.xml | 4 ++ app/src/main/res/values/strings.xml | 3 +- 8 files changed, 97 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 02913aa..e953dc9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Please take care, that this app might be unstable due to it's early development ## LICENSE ``` -Teleposter Copyright © 2016 Jan-Lukas Else +Teleposter Copyright © 2016 - 2017 Jan-Lukas Else 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 3 of the License, or (at your option) any later version. diff --git a/app/build.gradle b/app/build.gradle index d4446ab..ddc2584 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "telegra.ph" minSdkVersion 15 targetSdkVersion 26 - versionCode 8 - versionName "1.2.0" + versionCode 9 + versionName "1.3.0" resConfigs "en" } buildTypes { @@ -33,6 +33,8 @@ dependencies { 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' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f289c2c..a5b10b5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ + package="telegra.ph" + android:installLocation="auto"> Unit) { - Bridge.get("${ApiBase}getPage/$path?access_token=%s&return_content=true", accessToken).asString { response, s, bridgeException -> - if (!s.isNullOrBlank() && bridgeException == null) try { - callback(true, JSONObject(s).parsePageResponse()) + 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) } @@ -20,9 +23,9 @@ class Api { } 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).asString { response, s, bridgeException -> - if (!s.isNullOrBlank() && bridgeException == null) try { - callback(true, JSONObject(s).parsePageResponse()) + 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) } @@ -31,9 +34,9 @@ class Api { } 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).asString { response, s, bridgeException -> - if (!s.isNullOrBlank() && bridgeException == null) try { - callback(true, JSONObject(s).parsePageResponse()) + 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) } @@ -42,9 +45,9 @@ class Api { } fun createAccount(callback: (accessToken: String?) -> Unit) { - Bridge.get("${ApiBase}createAccount?short_name=teleposter").asString { response, s, bridgeException -> - if (!s.isNullOrBlank() && bridgeException == null) try { - callback(JSONObject(s).optJSONObject("result")?.optString("access_token")) + 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) } @@ -53,9 +56,9 @@ class Api { } fun getPageList(accessToken: String?, offset: Int = 0, callback: (success: Boolean, MutableList?) -> Unit) { - Bridge.get("${ApiBase}getPageList?access_token=%s&limit=200&offset=$offset", accessToken).asString { response, s, bridgeException -> - if (!s.isNullOrBlank() && bridgeException == null) try { - JSONObject(s).optJSONObject("result")?.let { + 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() @@ -122,6 +125,15 @@ class Api { } } + 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( diff --git a/app/src/main/java/telegra/ph/Editor.kt b/app/src/main/java/telegra/ph/Editor.kt index 23173b3..4db50b4 100644 --- a/app/src/main/java/telegra/ph/Editor.kt +++ b/app/src/main/java/telegra/ph/Editor.kt @@ -1,7 +1,7 @@ package telegra.ph +import android.annotation.SuppressLint import android.content.Context -import android.support.annotation.Keep import android.util.AttributeSet import android.webkit.JavascriptInterface import android.webkit.WebSettings @@ -23,13 +23,14 @@ class Editor : WebView { init() } + @SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface") private fun init() { this.settings.javaScriptEnabled = true this.settings.cacheMode = WebSettings.LOAD_NO_CACHE this.addJavascriptInterface(MyJavaScriptInterface(), "android") this.settings.loadWithOverviewMode = true this.settings.useWideViewPort = true - this.loadUrl("file:///android_asset/editor.html") + this.loadDataWithBaseURL("http://telegra.ph", context.assets.open("editor.html").bufferedReader().readText(), "text/html", "utf-8", null) } private inner class MyJavaScriptInterface { @@ -40,15 +41,19 @@ class Editor : WebView { } fun setText(html: String) { - setWebViewClient(object : WebViewClient() { + 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 getText(callback: (json: String?) -> Unit) { getCallback = callback this.loadUrl("javascript:getNodeJson();") diff --git a/app/src/main/java/telegra/ph/MainActivity.kt b/app/src/main/java/telegra/ph/MainActivity.kt index 948da89..7234abe 100644 --- a/app/src/main/java/telegra/ph/MainActivity.kt +++ b/app/src/main/java/telegra/ph/MainActivity.kt @@ -8,9 +8,13 @@ import android.view.Menu import android.view.MenuItem import android.view.View import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.folderselector.FileChooserDialog import im.delight.android.webview.AdvancedWebView +import pub.devrel.easypermissions.AfterPermissionGranted +import pub.devrel.easypermissions.EasyPermissions +import java.io.File -class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { +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? } @@ -33,7 +37,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { isVerticalScrollBarEnabled = false overScrollMode = View.OVER_SCROLL_NEVER } - if (accessToken().isNullOrBlank()) Api().createAccount { accessToken -> + if (accessToken().isNullOrBlank()) Api.createAccount { accessToken -> if (accessToken != null) saveAccessToken(accessToken) } if (intent.action == Intent.ACTION_VIEW && intent.dataString.contains("telegra.ph")) loadPage(intent.dataString.split("/").last()) @@ -50,7 +54,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { webView?.visibility = View.GONE currentPage = null // Load - if (path != null) Api().getPage(path, accessToken()) { success, page -> + if (path != null) Api.getPage(path, accessToken()) { success, page -> if (success) runOnUiThread { isEdit = true currentPage = page @@ -70,7 +74,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { editor?.visibility = View.GONE currentPage = null // Load - Api().getPage(path, accessToken()) { success, page -> + Api.getPage(path, accessToken()) { success, page -> if (success) showPage(page) else showError() } @@ -111,6 +115,17 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { } } + @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?) { } @@ -127,6 +142,15 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { 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() @@ -163,6 +187,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { menu?.findItem(R.id.create)?.isVisible = !editorMode menu?.findItem(R.id.share)?.isVisible = !editorMode menu?.findItem(R.id.try_edit)?.isVisible = !editorMode && !canEdit + menu?.findItem(R.id.image)?.isVisible = editorMode menu?.findItem(R.id.publish)?.isVisible = editorMode menu?.findItem(R.id.edit)?.isVisible = canEdit return true @@ -170,6 +195,10 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { + R.id.image -> { + uploadImage() + true + } R.id.create -> { loadEditor() true @@ -178,16 +207,16 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { editor?.getText { json -> MaterialDialog.Builder(this) .title(R.string.title_question) - .input(getString(R.string.title_hint), currentPage?.title ?: "", { dialog, title -> + .input(getString(R.string.title_hint), currentPage?.title ?: "", { _, title -> MaterialDialog.Builder(this) .title(R.string.name_question) - .input(getString(R.string.name_hint), if (isEdit) currentPage?.author_name ?: authorName() ?: "" else authorName() ?: "", { dialog, name -> + .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 (isEdit) Api.editPage(accessToken(), currentPage?.path, json, title.toString(), name.toString()) { success, page -> if (success) showPage(page) else showError() } - else Api().createPage(accessToken(), json, title.toString(), name.toString()) { success, page -> + else Api.createPage(accessToken(), json, title.toString(), name.toString()) { success, page -> if (success) showPage(page) else showError() } @@ -207,16 +236,16 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { .title(R.string.bookmarks) .positiveText(android.R.string.ok) .items(bookmarks().reversed().map { it.split("xxx;xxx")[1] }) - .itemsCallback { materialDialog, view, i, charSequence -> + .itemsCallback { _, _, i, _ -> loadPage(bookmarks().reversed().map { it.split("xxx;xxx")[0] }[i]) } - .itemsLongCallback { materialDialog, view, i, charSequence -> + .itemsLongCallback { _, _, i, _ -> MaterialDialog.Builder(this) .title(R.string.delete) .content(R.string.delete_question) .positiveText(android.R.string.yes) .negativeText(android.R.string.no) - .onPositive { materialDialog, dialogAction -> + .onPositive { _, _ -> deleteBookmark(bookmarks().reversed().map { it.split("xxx;xxx")[0] }[i]) } .show() @@ -226,14 +255,14 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { true } R.id.published -> { - Api().getPageList(accessToken()) { success, result -> + Api.getPageList(accessToken()) { success, result -> if (!success || result == null || result.isEmpty()) showError() else { MaterialDialog.Builder(this) .title(R.string.published) .positiveText(android.R.string.ok) .items(result.map(Page::title)) - .itemsCallback { materialDialog, view, i, charSequence -> + .itemsCallback { _, _, i, _ -> loadPage(result.map(Page::path)[i]) } .show() @@ -244,7 +273,7 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { R.id.bookmark -> { MaterialDialog.Builder(this) .title(R.string.title_question) - .input(getString(R.string.title_hint), "", { dialog, input -> + .input(getString(R.string.title_hint), "", { _, input -> addBookmark("${currentUrl.split("/").last()}xxx;xxx$input") }) .show() @@ -271,4 +300,8 @@ class MainActivity : AppCompatActivity(), AdvancedWebView.Listener { } } + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } } diff --git a/app/src/main/res/menu/activity_main.xml b/app/src/main/res/menu/activity_main.xml index 136c591..d2517f3 100644 --- a/app/src/main/res/menu/activity_main.xml +++ b/app/src/main/res/menu/activity_main.xml @@ -1,6 +1,10 @@ + Share Help - Why can\'t I add images?Unfortunately I wasn\'t able to add the feature yet.

Why can\'t I select headings?

Same reasons as above, but you can do it manually via the code editor.

Why can\'t I login with my Telegram account?

That\'s not possible!

Used libraries

Material Dialogs, Bridge, AdvancedWebView

About

This app is made by Jan-Lukas Else and it\'s code is published on Github.]]>
+ Why can\'t I select headings?Unfortunately I wasn\'t able to add the feature yet, but you can do it manually via the code editor.

Why can\'t I login with my Telegram account?

That\'s not possible!

Used libraries

Material Dialogs, Bridge, AdvancedWebView, EasyPermissions

About

This app is made by Jan-Lukas Else and it\'s code is published on Github.]]> Bookmarks Bookmark this Create @@ -25,4 +25,5 @@ Published posts Your name? Awesome Writer + Add image \ No newline at end of file