Skip to content

Commit 1c3fb0e

Browse files
committed
allow exporting clipboard items into a file
1 parent aea7b7b commit 1c3fb0e

File tree

8 files changed

+214
-7
lines changed

8 files changed

+214
-7
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
android:name="android.permission.USE_FINGERPRINT"
99
tools:node="remove" />
1010

11+
<uses-permission
12+
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
13+
android:maxSdkVersion="28" />
14+
1115
<application
1216
android:name=".App"
1317
android:allowBackup="true"

app/src/main/kotlin/com/simplemobiletools/keyboard/activities/MainActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import android.view.MenuItem
1010
import android.view.inputmethod.InputMethodManager
1111
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
1212
import com.simplemobiletools.commons.extensions.*
13+
import com.simplemobiletools.commons.helpers.LICENSE_GSON
1314
import com.simplemobiletools.commons.models.FAQItem
1415
import com.simplemobiletools.keyboard.BuildConfig
1516
import com.simplemobiletools.keyboard.R
@@ -62,7 +63,7 @@ class MainActivity : SimpleActivity() {
6263
}
6364

6465
private fun launchAbout() {
65-
val licenses = 0
66+
val licenses = LICENSE_GSON
6667

6768
val faqItems = arrayListOf(
6869
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),

app/src/main/kotlin/com/simplemobiletools/keyboard/activities/ManageClipboardItemsActivity.kt

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
package com.simplemobiletools.keyboard.activities
22

3+
import android.app.Activity
4+
import android.content.Intent
35
import android.os.Bundle
46
import android.view.Menu
57
import android.view.MenuItem
6-
import com.simplemobiletools.commons.extensions.beVisibleIf
7-
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
8-
import com.simplemobiletools.commons.extensions.underlineText
9-
import com.simplemobiletools.commons.extensions.updateTextColors
8+
import com.google.gson.Gson
9+
import com.simplemobiletools.commons.extensions.*
10+
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
1011
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
12+
import com.simplemobiletools.commons.helpers.isQPlus
1113
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
1214
import com.simplemobiletools.keyboard.R
1315
import com.simplemobiletools.keyboard.adapters.ClipsActivityAdapter
1416
import com.simplemobiletools.keyboard.dialogs.AddOrEditClipDialog
17+
import com.simplemobiletools.keyboard.dialogs.ExportClipsDialog
1518
import com.simplemobiletools.keyboard.extensions.clipsDB
19+
import com.simplemobiletools.keyboard.extensions.config
1620
import com.simplemobiletools.keyboard.models.Clip
1721
import kotlinx.android.synthetic.main.activity_manage_clipboard_items.*
22+
import java.io.File
23+
import java.io.OutputStream
1824

1925
class ManageClipboardItemsActivity : SimpleActivity(), RefreshRecyclerViewListener {
26+
private val PICK_EXPORT_CLIPS_INTENT = 21
2027

2128
override fun onCreate(savedInstanceState: Bundle?) {
2229
super.onCreate(savedInstanceState)
@@ -43,12 +50,26 @@ class ManageClipboardItemsActivity : SimpleActivity(), RefreshRecyclerViewListen
4350
override fun onOptionsItemSelected(item: MenuItem): Boolean {
4451
when (item.itemId) {
4552
R.id.add_clipboard_item -> addOrEditClip()
53+
R.id.export_clips -> exportClips()
54+
R.id.import_clips -> importClips()
4655
else -> return super.onOptionsItemSelected(item)
4756
}
4857

4958
return true
5059
}
5160

61+
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
62+
super.onActivityResult(requestCode, resultCode, resultData)
63+
if (requestCode == PICK_EXPORT_CLIPS_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
64+
val outputStream = contentResolver.openOutputStream(resultData.data!!)
65+
exportClipsTo(outputStream)
66+
}
67+
}
68+
69+
override fun refreshItems() {
70+
updateClips()
71+
}
72+
5273
private fun updateClips() {
5374
ensureBackgroundThread {
5475
val clips = clipsDB.getClips().toMutableList() as ArrayList<Clip>
@@ -72,7 +93,52 @@ class ManageClipboardItemsActivity : SimpleActivity(), RefreshRecyclerViewListen
7293
}
7394
}
7495

75-
override fun refreshItems() {
76-
updateClips()
96+
private fun exportClips() {
97+
if (isQPlus()) {
98+
ExportClipsDialog(this, config.lastExportedClipsFolder, true) { path, filename ->
99+
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
100+
type = "text/plain"
101+
putExtra(Intent.EXTRA_TITLE, filename)
102+
addCategory(Intent.CATEGORY_OPENABLE)
103+
startActivityForResult(this, PICK_EXPORT_CLIPS_INTENT)
104+
}
105+
}
106+
} else {
107+
handlePermission(PERMISSION_WRITE_STORAGE) {
108+
if (it) {
109+
ExportClipsDialog(this, config.lastExportedClipsFolder, false) { path, filename ->
110+
val file = File(path)
111+
getFileOutputStream(file.toFileDirItem(this), true) {
112+
exportClipsTo(it)
113+
}
114+
}
115+
}
116+
}
117+
}
77118
}
119+
120+
private fun exportClipsTo(outputStream: OutputStream?) {
121+
if (outputStream == null) {
122+
toast(R.string.unknown_error_occurred)
123+
return
124+
}
125+
126+
ensureBackgroundThread {
127+
val clips = clipsDB.getClips().map { it.value }
128+
if (clips.isEmpty()) {
129+
toast(R.string.no_entries_for_exporting)
130+
return@ensureBackgroundThread
131+
}
132+
133+
134+
val json = Gson().toJson(clips)
135+
outputStream.bufferedWriter().use { out ->
136+
out.write(json)
137+
}
138+
139+
toast(R.string.exporting_successful)
140+
}
141+
}
142+
143+
private fun importClips() {}
78144
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.simplemobiletools.keyboard.dialogs
2+
3+
import androidx.appcompat.app.AlertDialog
4+
import com.simplemobiletools.commons.activities.BaseSimpleActivity
5+
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
6+
import com.simplemobiletools.commons.dialogs.FilePickerDialog
7+
import com.simplemobiletools.commons.extensions.*
8+
import com.simplemobiletools.keyboard.R
9+
import com.simplemobiletools.keyboard.extensions.config
10+
import kotlinx.android.synthetic.main.dialog_export_clips.view.*
11+
12+
class ExportClipsDialog(
13+
val activity: BaseSimpleActivity, path: String, val hidePath: Boolean,
14+
callback: (path: String, filename: String) -> Unit
15+
) {
16+
init {
17+
var folder = if (path.isNotEmpty() && activity.getDoesFilePathExist(path)) {
18+
path
19+
} else {
20+
activity.internalStoragePath
21+
}
22+
23+
val view = activity.layoutInflater.inflate(R.layout.dialog_export_clips, null).apply {
24+
export_clips_filename.setText("${activity.getString(R.string.app_launcher_name)}_${activity.getCurrentFormattedDateTime()}")
25+
26+
if (hidePath) {
27+
export_clips_path_label.beGone()
28+
export_clips_path.beGone()
29+
} else {
30+
export_clips_path.text = activity.humanizePath(folder)
31+
export_clips_path.setOnClickListener {
32+
FilePickerDialog(activity, folder, false, showFAB = true) {
33+
export_clips_path.text = activity.humanizePath(it)
34+
folder = it
35+
}
36+
}
37+
}
38+
}
39+
40+
AlertDialog.Builder(activity)
41+
.setPositiveButton(R.string.ok, null)
42+
.setNegativeButton(R.string.cancel, null)
43+
.create().apply {
44+
activity.setupDialogStuff(view, this, R.string.export_clipboard_items) {
45+
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
46+
val filename = view.export_clips_filename.value
47+
if (filename.isEmpty()) {
48+
activity.toast(R.string.filename_cannot_be_empty)
49+
return@setOnClickListener
50+
}
51+
52+
val newPath = "${folder.trimEnd('/')}/$filename"
53+
if (!newPath.getFilenameFromPath().isAValidFilename()) {
54+
activity.toast(R.string.filename_invalid_characters)
55+
return@setOnClickListener
56+
}
57+
58+
activity.config.lastExportedClipsFolder = folder
59+
if (!hidePath && activity.getDoesFilePathExist(newPath)) {
60+
val title = String.format(activity.getString(R.string.file_already_exists_overwrite), newPath.getFilenameFromPath())
61+
ConfirmationDialog(activity, title) {
62+
callback(newPath, filename)
63+
dismiss()
64+
}
65+
} else {
66+
callback(newPath, filename)
67+
dismiss()
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}

app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Config.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ class Config(context: Context) : BaseConfig(context) {
1515
var showPopupOnKeypress: Boolean
1616
get() = prefs.getBoolean(SHOW_POPUP_ON_KEYPRESS, true)
1717
set(showPopupOnKeypress) = prefs.edit().putBoolean(SHOW_POPUP_ON_KEYPRESS, showPopupOnKeypress).apply()
18+
19+
var lastExportedClipsFolder: String
20+
get() = prefs.getString(LAST_EXPORTED_CLIPS_FOLDER, "")!!
21+
set(lastExportedClipsFolder) = prefs.edit().putString(LAST_EXPORTED_CLIPS_FOLDER, lastExportedClipsFolder).apply()
1822
}

app/src/main/kotlin/com/simplemobiletools/keyboard/helpers/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const val MAX_KEYS_PER_MINI_ROW = 5
1010
// shared prefs
1111
const val VIBRATE_ON_KEYPRESS = "vibrate_on_keypress"
1212
const val SHOW_POPUP_ON_KEYPRESS = "show_popup_on_keypress"
13+
const val LAST_EXPORTED_CLIPS_FOLDER = "last_exported_clips_folder"
1314

1415
// differentiate current and pinned clips at the keyboards' Clipboard section
1516
const val ITEM_SECTION_LABEL = 0
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:id="@+id/export_clips_wrapper"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent">
6+
7+
<LinearLayout
8+
android:id="@+id/export_clips_holder"
9+
android:layout_width="match_parent"
10+
android:layout_height="wrap_content"
11+
android:orientation="vertical"
12+
android:paddingLeft="@dimen/activity_margin"
13+
android:paddingTop="@dimen/activity_margin"
14+
android:paddingRight="@dimen/activity_margin">
15+
16+
<com.simplemobiletools.commons.views.MyTextView
17+
android:id="@+id/export_clips_path_label"
18+
android:layout_width="wrap_content"
19+
android:layout_height="wrap_content"
20+
android:layout_marginStart="@dimen/small_margin"
21+
android:text="@string/path"
22+
android:textSize="@dimen/smaller_text_size" />
23+
24+
<com.simplemobiletools.commons.views.MyTextView
25+
android:id="@+id/export_clips_path"
26+
android:layout_width="match_parent"
27+
android:layout_height="wrap_content"
28+
android:layout_marginBottom="@dimen/small_margin"
29+
android:paddingStart="@dimen/small_margin"
30+
android:paddingTop="@dimen/small_margin"
31+
android:paddingBottom="@dimen/activity_margin" />
32+
33+
<com.simplemobiletools.commons.views.MyTextInputLayout
34+
android:id="@+id/export_clips_hint"
35+
android:layout_width="match_parent"
36+
android:layout_height="wrap_content"
37+
android:hint="@string/filename_without_txt">
38+
39+
<com.simplemobiletools.commons.views.MyEditText
40+
android:id="@+id/export_clips_filename"
41+
android:layout_width="match_parent"
42+
android:layout_height="wrap_content"
43+
android:layout_marginBottom="@dimen/activity_margin"
44+
android:singleLine="true"
45+
android:textCursorDrawable="@null"
46+
android:textSize="@dimen/normal_text_size" />
47+
48+
</com.simplemobiletools.commons.views.MyTextInputLayout>
49+
</LinearLayout>
50+
</ScrollView>

app/src/main/res/menu/menu_manage_clipboard_items.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,12 @@
66
android:icon="@drawable/ic_plus_vector"
77
android:title="@string/add_new_item"
88
app:showAsAction="ifRoom" />
9+
<item
10+
android:id="@+id/import_clips"
11+
android:title="@string/import_clipboard_items"
12+
app:showAsAction="never" />
13+
<item
14+
android:id="@+id/export_clips"
15+
android:title="@string/export_clipboard_items"
16+
app:showAsAction="never" />
917
</menu>

0 commit comments

Comments
 (0)