mirror of https://github.com/jlelse/teleposter
Added option to add images; improved code
This commit is contained in:
parent
7c2de6cf74
commit
ad5be8ed47
|
@ -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.
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue