Feature: Customize fonts
This commit is contained in:
@ -59,7 +59,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
}
|
||||
|
||||
setContentView(R.layout.tab_main)
|
||||
setContentView(R.layout.ui_main)
|
||||
toolbar = findViewById(R.id.terminal_toolbar) as Toolbar
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
package io.neoterm.ui.customization
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
@ -8,24 +10,32 @@ import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Spinner
|
||||
import android.widget.Toast
|
||||
import io.neoterm.R
|
||||
import io.neoterm.backend.TerminalSession
|
||||
import io.neoterm.customize.NeoTermPath
|
||||
import io.neoterm.customize.font.FontManager
|
||||
import io.neoterm.utils.FileUtils
|
||||
import io.neoterm.utils.MediaUtils
|
||||
import io.neoterm.utils.TerminalUtils
|
||||
import io.neoterm.view.BasicSessionCallback
|
||||
import io.neoterm.view.BasicViewClient
|
||||
import io.neoterm.view.TerminalView
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
class CustomizationActivity: AppCompatActivity() {
|
||||
class CustomizationActivity : AppCompatActivity() {
|
||||
lateinit var terminalView: TerminalView
|
||||
lateinit var viewClient: BasicViewClient
|
||||
lateinit var sessionCallback: BasicSessionCallback
|
||||
lateinit var session: TerminalSession
|
||||
|
||||
val REQUEST_SELECT_FONT = 22222
|
||||
val REQUEST_SELECT_COLOR = 22223
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.ui_customization)
|
||||
@ -41,6 +51,23 @@ class CustomizationActivity: AppCompatActivity() {
|
||||
arrayOf("echo", "Hello NeoTerm."), null, null, sessionCallback, false)
|
||||
terminalView.attachSession(session)
|
||||
|
||||
findViewById(R.id.custom_install_font_button).setOnClickListener {
|
||||
val intent = Intent()
|
||||
intent.action = Intent.ACTION_GET_CONTENT
|
||||
intent.type = "*/*"
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.install_font)), REQUEST_SELECT_FONT)
|
||||
}
|
||||
|
||||
findViewById(R.id.custom_install_color_button).setOnClickListener {
|
||||
val intent = Intent()
|
||||
intent.action = Intent.ACTION_GET_CONTENT
|
||||
intent.type = "*/*"
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.install_color)), REQUEST_SELECT_COLOR)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSpinners() {
|
||||
FontManager.refreshFontList()
|
||||
setupSpinner(R.id.custom_font_spinner, FontManager.getFontNames(), FontManager.getCurrentFontName(), object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
}
|
||||
@ -62,11 +89,52 @@ class CustomizationActivity: AppCompatActivity() {
|
||||
spinner.setSelection(if (data.contains(selected)) data.indexOf(selected) else 0)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupSpinners()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
session.finishIfRunning()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val selected = MediaUtils.getPath(this, data.data)
|
||||
if (selected != null && selected.isNotEmpty()) {
|
||||
when (requestCode) {
|
||||
REQUEST_SELECT_FONT -> installFont(selected)
|
||||
REQUEST_SELECT_COLOR -> installColor(selected)
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
private fun installColor(selected: String) {
|
||||
installFileTo(selected, NeoTermPath.COLORS_PATH)
|
||||
setupSpinners()
|
||||
}
|
||||
|
||||
private fun installFont(selected: String) {
|
||||
installFileTo(selected, NeoTermPath.FONT_PATH)
|
||||
setupSpinners()
|
||||
}
|
||||
|
||||
private fun installFileTo(file: String, targetDir: String) {
|
||||
try {
|
||||
val fileObject = File(file)
|
||||
val input = FileInputStream(fileObject.absolutePath)
|
||||
val targetFile = File(targetDir, fileObject.name)
|
||||
input.use {
|
||||
FileUtils.writeFile(targetFile, it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, getString(R.string.error) + ": ${e.localizedMessage}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||
when (item?.itemId) {
|
||||
android.R.id.home -> finish()
|
||||
|
139
app/src/main/java/io/neoterm/utils/MediaUtils.kt
Normal file
139
app/src/main/java/io/neoterm/utils/MediaUtils.kt
Normal file
@ -0,0 +1,139 @@
|
||||
package io.neoterm.utils
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
|
||||
object MediaUtils {
|
||||
/**
|
||||
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||
* Framework Documents, as well as the _data field for the MediaStore and
|
||||
* other file-based ContentProviders.
|
||||
|
||||
* @param context The context.
|
||||
* *
|
||||
* @param uri The Uri to query.
|
||||
*/
|
||||
fun getPath(context: Context, uri: Uri): String? {
|
||||
val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
if ("primary".equals(type, ignoreCase = true)) {
|
||||
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
|
||||
}
|
||||
|
||||
// TODO handle non-primary volumes
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
|
||||
val id = DocumentsContract.getDocumentId(uri)
|
||||
val contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)!!)
|
||||
|
||||
return getDataColumn(context, contentUri, null, null)
|
||||
} else if (isMediaDocument(uri)) {
|
||||
val docId = DocumentsContract.getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val type = split[0]
|
||||
|
||||
var contentUri: Uri? = null
|
||||
if ("image" == type) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
} else if ("video" == type) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
} else if ("audio" == type) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||
}
|
||||
|
||||
val selection = "_id=?"
|
||||
val selectionArgs = arrayOf(split[1])
|
||||
|
||||
return getDataColumn(context, contentUri!!, selection, selectionArgs)
|
||||
}// MediaProvider
|
||||
// DownloadsProvider
|
||||
} else if ("content".equals(uri.scheme, ignoreCase = true)) {
|
||||
return getDataColumn(context, uri, null, null)
|
||||
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
|
||||
return uri.path
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the data column for this Uri. This is useful for
|
||||
* MediaStore Uris, and other file-based ContentProviders.
|
||||
|
||||
* @param context The context.
|
||||
* *
|
||||
* @param uri The Uri to query.
|
||||
* *
|
||||
* @param selection (Optional) Filter used in the query.
|
||||
* *
|
||||
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||
* *
|
||||
* @return The value of the _data column, which is typically a file path.
|
||||
*/
|
||||
fun getDataColumn(context: Context, uri: Uri, selection: String?,
|
||||
selectionArgs: Array<String>?): String? {
|
||||
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
|
||||
try {
|
||||
cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val column_index = cursor.getColumnIndexOrThrow(column)
|
||||
return cursor.getString(column_index)
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* *
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
fun isExternalStorageDocument(uri: Uri): Boolean {
|
||||
return "com.android.externalstorage.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* *
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
fun isDownloadsDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.downloads.documents" == uri.authority
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* *
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
fun isMediaDocument(uri: Uri): Boolean {
|
||||
return "com.android.providers.media.documents" == uri.authority
|
||||
}
|
||||
}
|
BIN
app/src/main/res/drawable-hdpi/ic_customization_white_36dp.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_customization_white_36dp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 637 B |
BIN
app/src/main/res/drawable-hdpi/ic_install_white_36.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_install_white_36.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 691 B |
@ -14,6 +14,7 @@
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginBottom="@dimen/text_margin"
|
||||
android:id="@+id/custom_editor_layout"
|
||||
style="?buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
@ -22,40 +23,71 @@
|
||||
android:layout_margin="@dimen/text_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/text_margin"
|
||||
android:layout_height="@dimen/custom_editor_line_height"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
style="?buttonBarButtonStyle"
|
||||
android:id="@+id/custom_install_font_button"
|
||||
android:layout_width="@dimen/custom_install_icon_width"
|
||||
android:layout_height="@dimen/custom_editor_line_height"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:src="@drawable/ic_install_white_36" />
|
||||
|
||||
<TextView
|
||||
android:gravity="center"
|
||||
android:id="@+id/custom_font_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="@string/pref_customization_font" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/custom_font_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/text_margin" />
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/text_margin"
|
||||
android:layout_toEndOf="@id/custom_font_text"
|
||||
android:layout_toStartOf="@id/custom_install_font_button" />
|
||||
|
||||
<LinearLayout
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="@dimen/custom_editor_line_height"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
style="?buttonBarButtonStyle"
|
||||
android:id="@+id/custom_install_color_button"
|
||||
android:layout_width="@dimen/custom_install_icon_width"
|
||||
android:layout_height="@dimen/custom_editor_line_height"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:src="@drawable/ic_install_white_36" />
|
||||
|
||||
<TextView
|
||||
android:gravity="center"
|
||||
android:id="@+id/custom_color_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="@string/pref_customization_color_scheme" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/custom_color_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/text_margin" />
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="@dimen/text_margin"
|
||||
android:layout_toEndOf="@id/custom_color_text"
|
||||
android:layout_toStartOf="@id/custom_install_color_button" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
|
@ -66,4 +66,6 @@
|
||||
<string name="general_settings_desc">响铃,振动,Shell</string>
|
||||
<string name="ui_settings_desc">全屏,标题栏,快捷键盘</string>
|
||||
<string name="package_settings_desc">源,更新,升级</string>
|
||||
<string name="install_font">安装字体</string>
|
||||
<string name="install_color">安装配色方案</string>
|
||||
</resources>
|
@ -7,4 +7,7 @@
|
||||
<dimen name="eks_height_two_line">72dp</dimen>
|
||||
<dimen name="eks_height_one_line">36dp</dimen>
|
||||
<dimen name="terminal_dialog_height">256dp</dimen>
|
||||
<dimen name="custom_editor_line_height">48dp</dimen>
|
||||
<dimen name="custom_install_icon_width">36dp</dimen>
|
||||
<dimen name="custom_install_icon_height">36dp</dimen>
|
||||
</resources>
|
||||
|
@ -18,6 +18,23 @@
|
||||
<string name="customization_settings_desc">Font, ColorScheme, ExtraKeys</string>
|
||||
<string name="customization_settings">Customization</string>
|
||||
|
||||
<string name="toggle_ime">Toggle IME</string>
|
||||
<string name="shell_not_found">Shell %s not found, please install it first.</string>
|
||||
<string name="fullscreen_mode_changed">FullScreen mode changed, please restart NeoTerm.</string>
|
||||
<string name="permission_denied">NeoTerm cannot get essential permissions, exiting.</string>
|
||||
<string name="error">Error</string>
|
||||
<string name="use_system_shell">System Shell</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="source_changed">APT source changed, you may get it updated by executing: apt update</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="package_details">Package: %s\nVersion: %s\nDepends: %s\nInstalled Size: %s\nDescription: %s\nHome Page: %s</string>
|
||||
<string name="package_list_empty">Package list is empty, please check out your source.</string>
|
||||
<string name="menu_refresh_list">Refresh</string>
|
||||
<string name="menu_update">Update, Refresh</string>
|
||||
<string name="install_font">Install Font</string>
|
||||
<string name="install_color">Install Color Scheme</string>
|
||||
|
||||
<string name="installer_message">Installing</string>
|
||||
<string name="installer_install_zsh_manually">You may install zsh and setup suggestions by executing: ~/install-zsh.sh</string>
|
||||
|
||||
@ -44,20 +61,6 @@
|
||||
<string name="pref_customization_color_scheme">Color Scheme</string>
|
||||
<string name="pref_customization_eks">Extra Keys</string>
|
||||
<string name="pref_package_source">Source</string>
|
||||
<string name="toggle_ime">Toggle IME</string>
|
||||
<string name="shell_not_found">Shell %s not found, please install it first.</string>
|
||||
<string name="fullscreen_mode_changed">FullScreen mode changed, please restart NeoTerm.</string>
|
||||
<string name="permission_denied">NeoTerm cannot get essential permissions, exiting.</string>
|
||||
<string name="error">Error</string>
|
||||
<string name="use_system_shell">System Shell</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="source_changed">APT source changed, you may get it updated by executing: apt update</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="package_details">Package: %s\nVersion: %s\nDepends: %s\nInstalled Size: %s\nDescription: %s\nHome Page: %s</string>
|
||||
<string name="package_list_empty">Package list is empty, please check out your source.</string>
|
||||
<string name="menu_refresh_list">Refresh</string>
|
||||
<string name="menu_update">Update, Refresh</string>
|
||||
|
||||
<string-array name="pref_general_shell_entries" translatable="false">
|
||||
<item>sh</item>
|
||||
|
Reference in New Issue
Block a user