Merge pull request #1125 from KryptKode/feat/import_export_blocked_nums
Add feature to import/export blocked numbers for dialer
This commit is contained in:
commit
e3c531cbd1
9 changed files with 329 additions and 4 deletions
|
@ -1,23 +1,30 @@
|
|||
package com.simplemobiletools.commons.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import com.simplemobiletools.commons.R
|
||||
import com.simplemobiletools.commons.adapters.ManageBlockedNumbersAdapter
|
||||
import com.simplemobiletools.commons.dialogs.AddBlockedNumberDialog
|
||||
import com.simplemobiletools.commons.dialogs.ExportBlockedNumbersDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.APP_ICON_IDS
|
||||
import com.simplemobiletools.commons.helpers.APP_LAUNCHER_NAME
|
||||
import com.simplemobiletools.commons.helpers.REQUEST_CODE_SET_DEFAULT_DIALER
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import com.simplemobiletools.commons.helpers.BlockedNumbersExporter.ExportResult
|
||||
import com.simplemobiletools.commons.interfaces.RefreshRecyclerViewListener
|
||||
import com.simplemobiletools.commons.models.BlockedNumber
|
||||
import kotlinx.android.synthetic.main.activity_manage_blocked_numbers.*
|
||||
import java.io.FileOutputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
||||
class ManageBlockedNumbersActivity : BaseSimpleActivity(), RefreshRecyclerViewListener {
|
||||
private val PICK_IMPORT_SOURCE_INTENT = 11
|
||||
private val PICK_EXPORT_FILE_INTENT = 21
|
||||
|
||||
override fun getAppIconIDs() = intent.getIntegerArrayListExtra(APP_ICON_IDS) ?: ArrayList()
|
||||
|
||||
override fun getAppLauncherName() = intent.getStringExtra(APP_LAUNCHER_NAME) ?: ""
|
||||
|
@ -51,6 +58,8 @@ class ManageBlockedNumbersActivity : BaseSimpleActivity(), RefreshRecyclerViewLi
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.add_blocked_number -> addOrEditBlockedNumber()
|
||||
R.id.import_blocked_numbers -> tryImportBlockedNumbers()
|
||||
R.id.export_blocked_numbers -> tryExportBlockedNumbers()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
|
@ -87,11 +96,113 @@ class ManageBlockedNumbersActivity : BaseSimpleActivity(), RefreshRecyclerViewLi
|
|||
}
|
||||
}
|
||||
|
||||
private fun tryImportBlockedNumbers() {
|
||||
if (isQPlus()) {
|
||||
Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "text/plain"
|
||||
startActivityForResult(this, PICK_IMPORT_SOURCE_INTENT)
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_READ_STORAGE) {
|
||||
if (it) {
|
||||
pickFileToImportBlockedNumbers()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImportBlockedNumbersFromFile(uri: Uri) {
|
||||
when (uri.scheme) {
|
||||
"file" -> importBlockedNumbers(uri.path!!)
|
||||
"content" -> {
|
||||
val tempFile = getTempFile("blocked", "blocked_numbers.txt")
|
||||
if (tempFile == null) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val inputStream = contentResolver.openInputStream(uri)
|
||||
val out = FileOutputStream(tempFile)
|
||||
inputStream!!.copyTo(out)
|
||||
importBlockedNumbers(tempFile.absolutePath)
|
||||
} catch (e: Exception) {
|
||||
showErrorToast(e)
|
||||
}
|
||||
}
|
||||
else -> toast(R.string.invalid_file_format)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickFileToImportBlockedNumbers() {
|
||||
FilePickerDialog(this) {
|
||||
importBlockedNumbers(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun importBlockedNumbers(path: String) {
|
||||
ensureBackgroundThread {
|
||||
val result = BlockedNumbersImporter(this).importBlockedNumbers(path)
|
||||
toast(when (result) {
|
||||
BlockedNumbersImporter.ImportResult.IMPORT_OK -> R.string.importing_successful
|
||||
BlockedNumbersImporter.ImportResult.IMPORT_FAIL -> R.string.no_items_found
|
||||
})
|
||||
updateBlockedNumbers()
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryExportBlockedNumbers() {
|
||||
if (isQPlus()) {
|
||||
ExportBlockedNumbersDialog(this, baseConfig.lastBlockedNumbersExportPath, true) { file ->
|
||||
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TITLE, file.name)
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
startActivityForResult(this, PICK_EXPORT_FILE_INTENT)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handlePermission(PERMISSION_WRITE_STORAGE) {
|
||||
if (it) {
|
||||
ExportBlockedNumbersDialog(this, baseConfig.lastBlockedNumbersExportPath, false) { file ->
|
||||
getFileOutputStream(file.toFileDirItem(this), true) { out ->
|
||||
exportBlockedNumbersTo(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportBlockedNumbersTo(outputStream: OutputStream?) {
|
||||
ensureBackgroundThread {
|
||||
val blockedNumbers = getBlockedNumbers()
|
||||
if (blockedNumbers.isEmpty()) {
|
||||
toast(R.string.no_entries_for_exporting)
|
||||
} else {
|
||||
BlockedNumbersExporter().exportBlockedNumbers(blockedNumbers, outputStream) {
|
||||
toast(
|
||||
when (it) {
|
||||
ExportResult.EXPORT_OK -> R.string.exporting_successful
|
||||
ExportResult.EXPORT_FAIL -> R.string.exporting_failed
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, resultData)
|
||||
if (requestCode == REQUEST_CODE_SET_DEFAULT_DIALER && isDefaultDialer()) {
|
||||
updatePlaceholderTexts()
|
||||
updateBlockedNumbers()
|
||||
} else if (requestCode == PICK_IMPORT_SOURCE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
|
||||
tryImportBlockedNumbersFromFile(resultData.data!!)
|
||||
} else if (requestCode == PICK_EXPORT_FILE_INTENT && resultCode == Activity.RESULT_OK && resultData != null && resultData.data != null) {
|
||||
val outputStream = contentResolver.openOutputStream(resultData.data!!)
|
||||
exportBlockedNumbersTo(outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package com.simplemobiletools.commons.dialogs
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.simplemobiletools.commons.R
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.helpers.*
|
||||
import kotlinx.android.synthetic.main.dialog_export_blocked_numbers.view.*
|
||||
import java.io.File
|
||||
|
||||
class ExportBlockedNumbersDialog(
|
||||
val activity: BaseSimpleActivity,
|
||||
val path: String,
|
||||
val hidePath: Boolean,
|
||||
callback: (file: File) -> Unit,
|
||||
) {
|
||||
private var realPath = if (path.isEmpty()) activity.internalStoragePath else path
|
||||
private val config = activity.baseConfig
|
||||
|
||||
init {
|
||||
val view = activity.layoutInflater.inflate(R.layout.dialog_export_blocked_numbers, null).apply {
|
||||
export_blocked_numbers_folder.text = activity.humanizePath(realPath)
|
||||
export_blocked_numbers_filename.setText("${activity.getString(R.string.blocked_numbers)}_${activity.getCurrentFormattedDateTime()}")
|
||||
|
||||
if (hidePath) {
|
||||
export_blocked_numbers_folder_label.beGone()
|
||||
export_blocked_numbers_folder.beGone()
|
||||
} else {
|
||||
export_blocked_numbers_folder.setOnClickListener {
|
||||
FilePickerDialog(activity, realPath, false, showFAB = true) {
|
||||
export_blocked_numbers_folder.text = activity.humanizePath(it)
|
||||
realPath = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog.Builder(activity)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create().apply {
|
||||
activity.setupDialogStuff(view, this, R.string.export_blocked_numbers) {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||
val filename = view.export_blocked_numbers_filename.value
|
||||
when {
|
||||
filename.isEmpty() -> activity.toast(R.string.empty_name)
|
||||
filename.isAValidFilename() -> {
|
||||
val file = File(realPath, "$filename$BLOCKED_NUMBERS_EXPORT_EXTENSION")
|
||||
if (!hidePath && file.exists()) {
|
||||
activity.toast(R.string.name_taken)
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
ensureBackgroundThread {
|
||||
config.lastBlockedNumbersExportPath = file.absolutePath.getParentPath()
|
||||
callback(file)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
else -> activity.toast(R.string.invalid_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1073,3 +1073,15 @@ fun AppCompatActivity.showSideloadingDialog() {
|
|||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseSimpleActivity.getTempFile(folderName: String, fileName: String): File? {
|
||||
val folder = File(cacheDir, fileName)
|
||||
if (!folder.exists()) {
|
||||
if (!folder.mkdir()) {
|
||||
toast(R.string.unknown_error_occurred)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return File(folder, fileName)
|
||||
}
|
||||
|
|
|
@ -414,6 +414,10 @@ open class BaseConfig(val context: Context) {
|
|||
get() = prefs.getString(LAST_EXPORTED_SETTINGS_FILE, "")!!
|
||||
set(lastExportedSettingsFile) = prefs.edit().putString(LAST_EXPORTED_SETTINGS_FILE, lastExportedSettingsFile).apply()
|
||||
|
||||
var lastBlockedNumbersExportPath: String
|
||||
get() = prefs.getString(LAST_BLOCKED_NUMBERS_EXPORT_PATH, "")!!
|
||||
set(lastBlockedNumbersExportPath) = prefs.edit().putString(LAST_BLOCKED_NUMBERS_EXPORT_PATH, lastBlockedNumbersExportPath).apply()
|
||||
|
||||
var fontSize: Int
|
||||
get() = prefs.getInt(FONT_SIZE, context.resources.getInteger(R.integer.default_font_size))
|
||||
set(size) = prefs.edit().putInt(FONT_SIZE, size).apply()
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package com.simplemobiletools.commons.helpers
|
||||
|
||||
import com.simplemobiletools.commons.models.BlockedNumber
|
||||
import java.io.OutputStream
|
||||
import java.util.ArrayList
|
||||
|
||||
class BlockedNumbersExporter {
|
||||
enum class ExportResult {
|
||||
EXPORT_FAIL, EXPORT_OK
|
||||
}
|
||||
|
||||
fun exportBlockedNumbers(
|
||||
blockedNumbers: ArrayList<BlockedNumber>,
|
||||
outputStream: OutputStream?,
|
||||
callback: (result: ExportResult) -> Unit,
|
||||
) {
|
||||
if (outputStream == null) {
|
||||
callback.invoke(ExportResult.EXPORT_FAIL)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
outputStream.bufferedWriter().use { out ->
|
||||
out.write(blockedNumbers.joinToString(BLOCKED_NUMBERS_EXPORT_DELIMITER) {
|
||||
it.number
|
||||
})
|
||||
}
|
||||
callback.invoke(ExportResult.EXPORT_OK)
|
||||
} catch (e: Exception) {
|
||||
callback.invoke(ExportResult.EXPORT_FAIL)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.simplemobiletools.commons.helpers
|
||||
|
||||
import android.telephony.PhoneNumberUtils
|
||||
import com.simplemobiletools.commons.activities.BaseSimpleActivity
|
||||
import com.simplemobiletools.commons.extensions.addBlockedNumber
|
||||
import com.simplemobiletools.commons.extensions.showErrorToast
|
||||
import java.io.File
|
||||
|
||||
class BlockedNumbersImporter(
|
||||
private val activity: BaseSimpleActivity,
|
||||
) {
|
||||
enum class ImportResult {
|
||||
IMPORT_FAIL, IMPORT_OK
|
||||
}
|
||||
|
||||
fun importBlockedNumbers(path: String): ImportResult {
|
||||
return try {
|
||||
val inputStream = File(path).inputStream()
|
||||
val numbers = inputStream.bufferedReader().use {
|
||||
val content = it.readText().split(BLOCKED_NUMBERS_EXPORT_DELIMITER)
|
||||
content.filter { text -> PhoneNumberUtils.isGlobalPhoneNumber(text) }
|
||||
}
|
||||
if (numbers.isNotEmpty()) {
|
||||
numbers.forEach { number ->
|
||||
activity.addBlockedNumber(number)
|
||||
}
|
||||
ImportResult.IMPORT_OK
|
||||
} else {
|
||||
ImportResult.IMPORT_FAIL
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
activity.showErrorToast(e)
|
||||
ImportResult.IMPORT_FAIL
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@ const val IS_FROM_GALLERY = "is_from_gallery"
|
|||
const val BROADCAST_REFRESH_MEDIA = "com.simplemobiletools.REFRESH_MEDIA"
|
||||
const val REFRESH_PATH = "refresh_path"
|
||||
const val IS_CUSTOMIZING_COLORS = "is_customizing_colors"
|
||||
const val BLOCKED_NUMBERS_EXPORT_DELIMITER = ","
|
||||
const val BLOCKED_NUMBERS_EXPORT_EXTENSION = ".txt"
|
||||
const val NOMEDIA = ".nomedia"
|
||||
const val YOUR_ALARM_SOUNDS_MIN_ID = 1000
|
||||
const val SHOW_FAQ_BEFORE_MAIL = "show_faq_before_mail"
|
||||
|
@ -141,6 +143,7 @@ const val LAST_RENAME_USED = "last_rename_used"
|
|||
const val LAST_RENAME_PATTERN_USED = "last_rename_pattern_used"
|
||||
const val LAST_EXPORTED_SETTINGS_FOLDER = "last_exported_settings_folder"
|
||||
const val LAST_EXPORTED_SETTINGS_FILE = "last_exported_settings_file"
|
||||
const val LAST_BLOCKED_NUMBERS_EXPORT_PATH = "last_blocked_numbers_export_path"
|
||||
const val FONT_SIZE = "font_size"
|
||||
const val WAS_MESSENGER_RECORDER_SHOWN = "was_messenger_recorder_shown"
|
||||
const val DEFAULT_TAB = "default_tab"
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/export_blocked_numbers_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/export_blocked_numbers_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/activity_margin"
|
||||
android:paddingRight="@dimen/activity_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_blocked_numbers_folder_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/small_margin"
|
||||
android:text="@string/folder"
|
||||
android:textSize="@dimen/smaller_text_size" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/export_blocked_numbers_folder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/small_margin"
|
||||
android:paddingStart="@dimen/small_margin"
|
||||
android:paddingTop="@dimen/small_margin"
|
||||
android:paddingBottom="@dimen/activity_margin" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextInputLayout
|
||||
android:id="@+id/export_blocked_numbers_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/filename">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/export_blocked_numbers_filename"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/activity_margin"
|
||||
android:singleLine="true"
|
||||
android:textCursorDrawable="@null"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</com.simplemobiletools.commons.views.MyTextInputLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
|
@ -6,4 +6,14 @@
|
|||
android:icon="@drawable/ic_plus_vector"
|
||||
android:title="@string/add_a_blocked_number"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/import_blocked_numbers"
|
||||
android:title="@string/import_blocked_numbers"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/export_blocked_numbers"
|
||||
android:title="@string/export_blocked_numbers"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
|
Loading…
Reference in a new issue