Added option to add images; improved code

This commit is contained in:
Jan-Lk Else 2017-07-16 08:04:35 +02:00
parent 7c2de6cf74
commit ad5be8ed47
8 changed files with 97 additions and 39 deletions

View File

@ -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.

View File

@ -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'

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="telegra.ph">
package="telegra.ph"
android:installLocation="auto">
<uses-feature
android:name="android.hardware.touchscreen"

View File

@ -1,17 +1,20 @@
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
class Api {
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).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<Page>?) -> 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<Page>()
@ -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(

View File

@ -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();")

View File

@ -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<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
}
}

View File

@ -1,6 +1,10 @@
<?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" />
<item
android:id="@+id/create"
android:title="@string/create"

View File

@ -3,7 +3,7 @@
<string name="share">Share</string>
<string name="help">Help</string>
<string name="help_text">
<![CDATA[<h3>Why can\'t I add images?</h3>Unfortunately I wasn\'t able to add the feature yet.<h3>Why can\'t I select headings?</h3>Same reasons as above, 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><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>
<![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="bookmarks">Bookmarks</string>
<string name="bookmark_this">Bookmark this</string>
<string name="create">Create</string>
@ -25,4 +25,5 @@
<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>
</resources>