diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5f3bc8b..054a795 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,11 +30,11 @@ dependencies { implementation(fileTree("libs")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.recyclerview:recyclerview:1.3.0") - implementation("androidx.activity:activity-ktx:1.6.1") - implementation("androidx.fragment:fragment-ktx:1.5.5") + implementation("androidx.activity:activity-ktx:1.7.0") + implementation("androidx.fragment:fragment-ktx:1.5.6") implementation("androidx.browser:browser:1.5.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") @@ -42,15 +42,16 @@ dependencies { implementation("androidx.navigation:navigation-ui-ktx:2.5.3") implementation("androidx.preference:preference-ktx:1.2.0") implementation("androidx.appcompat:appcompat:1.6.1") - implementation("androidx.work:work-runtime-ktx:2.8.0") - implementation("androidx.work:work-multiprocess:2.8.0") + implementation("androidx.work:work-runtime-ktx:2.8.1") + implementation("androidx.work:work-multiprocess:2.8.1") implementation("com.google.android.material:material:1.8.0") - implementation("com.google.code.gson:gson:2.8.9") + implementation("com.google.code.gson:gson:2.9.0") implementation("com.github.jenly1314:zxing-lite:2.1.1") - implementation("com.afollestad.material-dialogs:core:3.3.0") - implementation("com.afollestad.material-dialogs:input:3.3.0") + implementation("com.blacksquircle.ui:editorkit:2.6.0") + implementation("com.blacksquircle.ui:language-base:2.6.0") + implementation("com.blacksquircle.ui:language-json:2.6.0") implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.3") implementation("org.yaml:snakeyaml:1.30") @@ -68,11 +69,11 @@ dependencies { exclude(group = "com.google.guava", module = "guava") } - implementation("androidx.room:room-runtime:2.5.0") - kapt("androidx.room:room-compiler:2.5.0") - implementation("androidx.room:room-ktx:2.5.0") + implementation("androidx.room:room-runtime:2.5.1") + kapt("androidx.room:room-compiler:2.5.1") + implementation("androidx.room:room-ktx:2.5.1") implementation("com.github.MatrixDev.Roomigrant:RoomigrantLib:0.3.4") kapt("com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4") - coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 09ea468..4607b16 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:installLocation="internalOnly"> - + + @@ -152,9 +155,6 @@ - diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt index e6a1e13..d84be13 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt @@ -91,7 +91,10 @@ object Key { const val SERVER_ENCRYPTION = "serverEncryption" const val SERVER_ALPN = "serverALPN" const val SERVER_CERTIFICATES = "serverCertificates" + const val SERVER_CONFIG = "serverConfig" + const val SERVER_CUSTOM = "serverCustom" + const val SERVER_CUSTOM_OUTBOUND = "serverCustomOutbound" const val SERVER_SECURITY_CATEGORY = "serverSecurityCategory" const val SERVER_TLS_CAMOUFLAGE_CATEGORY = "serverTlsCamouflageCategory" diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt index dbb74a0..41ad25c 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt @@ -232,6 +232,8 @@ object DataStore : OnPreferenceDataStoreChangeListener { var landingProxyTmp by profileCacheStore.stringToInt(Key.GROUP_LANDING_PROXY) var serverConfig by profileCacheStore.string(Key.SERVER_CONFIG) + var serverCustom by profileCacheStore.string(Key.SERVER_CUSTOM) + var serverCustomOutbound by profileCacheStore.string(Key.SERVER_CUSTOM_OUTBOUND) var groupName by profileCacheStore.string(Key.GROUP_NAME) var groupType by profileCacheStore.stringToInt(Key.GROUP_TYPE) diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt index ece2d99..705b53a 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt @@ -101,7 +101,7 @@ fun buildConfig( val globalOutbounds = HashMap() val selectorNames = ArrayList() val group = SagerDatabase.groupDao.getById(proxy.groupId) - var optionsToMerge = "" + val optionsToMerge = proxy.requireBean().customConfigJson ?: "" fun ProxyEntity.resolveChainInternal(): MutableList { val bean = requireBean() @@ -424,9 +424,6 @@ fun buildConfig( if (bean.customOutboundJson.isNotBlank()) { mergeJSON(bean.customOutboundJson, currentOutbound) } - if (index == 0 && bean.customConfigJson.isNotBlank()) { - optionsToMerge = bean.customConfigJson - } } pastEntity?.requireBean()?.apply { diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/WebviewFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/WebviewFragment.kt index 79cdebd..c2e7055 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/WebviewFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/WebviewFragment.kt @@ -6,9 +6,9 @@ import android.text.InputType import android.view.MenuItem import android.view.View import android.webkit.* +import android.widget.EditText import androidx.appcompat.widget.Toolbar -import com.afollestad.materialdialogs.MaterialDialog -import com.afollestad.materialdialogs.input.input +import com.google.android.material.dialog.MaterialAlertDialogBuilder import io.nekohasekai.sagernet.BuildConfig import io.nekohasekai.sagernet.R import io.nekohasekai.sagernet.database.DataStore @@ -55,17 +55,18 @@ class WebviewFragment : ToolbarFragment(R.layout.layout_webview), Toolbar.OnMenu override fun onMenuItemClick(item: MenuItem): Boolean { when (item.itemId) { R.id.action_set_url -> { - MaterialDialog(requireContext()).show { - title(R.string.set_panel_url) - input( - prefill = DataStore.yacdURL, - inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI - ) { _, str -> - DataStore.yacdURL = str.toString() + val view = EditText(context).apply { + inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI + setText(DataStore.yacdURL) + } + MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.set_panel_url) + .setView(view) + .setPositiveButton(android.R.string.ok) { _, _ -> + DataStore.yacdURL = view.text.toString() mWebView.loadUrl(DataStore.yacdURL) } - positiveButton(R.string.save) - } + .setNegativeButton(android.R.string.cancel, null) + .show() } R.id.close -> { mWebView.onPause() diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ConfigEditActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ConfigEditActivity.kt new file mode 100644 index 0000000..5b66d44 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ConfigEditActivity.kt @@ -0,0 +1,146 @@ +package io.nekohasekai.sagernet.ui.profile + +import android.annotation.SuppressLint +import android.content.DialogInterface +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import androidx.appcompat.app.AlertDialog +import com.blacksquircle.ui.editorkit.insert +import com.blacksquircle.ui.language.json.JsonLanguage +import com.github.shadowsocks.plugin.Empty +import com.github.shadowsocks.plugin.fragment.AlertDialogFragment +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import io.nekohasekai.sagernet.Key +import io.nekohasekai.sagernet.R +import io.nekohasekai.sagernet.database.DataStore +import io.nekohasekai.sagernet.databinding.LayoutEditConfigBinding +import io.nekohasekai.sagernet.ktx.* +import io.nekohasekai.sagernet.ui.ThemedActivity +import moe.matsuri.nb4a.ui.ExtendedKeyboard +import org.json.JSONObject + +class ConfigEditActivity : ThemedActivity() { + + var dirty = false + var key = Key.SERVER_CONFIG + + class UnsavedChangesDialogFragment : AlertDialogFragment() { + override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) { + setTitle(R.string.unsaved_changes_prompt) + setPositiveButton(R.string.yes) { _, _ -> + (requireActivity() as ConfigEditActivity).saveAndExit() + } + setNegativeButton(R.string.no) { _, _ -> + requireActivity().finish() + } + setNeutralButton(android.R.string.cancel, null) + } + } + + lateinit var binding: LayoutEditConfigBinding + + @SuppressLint("InlinedApi") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + intent?.extras?.apply { + getString("key")?.let { key = it } + } + + binding = LayoutEditConfigBinding.inflate(layoutInflater) + setContentView(binding.root) + + setSupportActionBar(findViewById(R.id.toolbar)) + supportActionBar?.apply { + setTitle(R.string.config_settings) + setDisplayHomeAsUpEnabled(true) + setHomeAsUpIndicator(R.drawable.ic_navigation_close) + } + +// binding.editor.colorScheme = mkTheme() + binding.editor.language = JsonLanguage() +// binding.editor.onChangeListener = OnChangeListener { +// config = binding.editor.text.toString() +// if (!dirty) { +// dirty = true +// DataStore.dirty = true +// } +// } + binding.editor.setHorizontallyScrolling(true) + binding.actionTab.setOnClickListener { + binding.editor.insert(binding.editor.tab()) + } + binding.actionUndo.setOnClickListener { + try { + binding.editor.undo() + } catch (_: Exception) { + } + } + binding.actionRedo.setOnClickListener { + try { + binding.editor.redo() + } catch (_: Exception) { + } + } + binding.actionFormat.setOnClickListener { + formatText()?.let { + binding.editor.setTextContent(it) + } + } + + val extendedKeyboard = findViewById(R.id.extended_keyboard) + extendedKeyboard.setKeyListener { char -> binding.editor.insert(char) } + extendedKeyboard.setHasFixedSize(true) + extendedKeyboard.submitList("{},:_\"".map { it.toString() }) + extendedKeyboard.setBackgroundColor(getColorAttr(R.attr.primaryOrTextPrimary)) + + binding.editor.setTextContent(DataStore.profileCacheStore.getString(key)!!) + } + + fun formatText(): String? { + try { + val txt = binding.editor.text.toString() + if (txt.isBlank()) { + return "" + } + return JSONObject(txt).toStringPretty() + } catch (e: Exception) { + MaterialAlertDialogBuilder(this).setTitle(R.string.error_title) + .setMessage(e.readableMessage).show() + return null + } + } + + fun saveAndExit() { + formatText()?.let { + DataStore.profileCacheStore.putString(key, it) + finish() + } + } + + override fun onBackPressed() { + if (dirty) UnsavedChangesDialogFragment().apply { key() } + .show(supportFragmentManager, null) else super.onBackPressed() + } + + override fun onSupportNavigateUp(): Boolean { + if (!super.onSupportNavigateUp()) finish() + return true + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.profile_apply_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_apply -> { + saveAndExit() + return true + } + } + return super.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ProfileSettingsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ProfileSettingsActivity.kt index 8b1da23..1d138eb 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ProfileSettingsActivity.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/profile/ProfileSettingsActivity.kt @@ -6,11 +6,13 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.os.Parcelable -import android.text.InputType import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.LinearLayout +import androidx.activity.result.component1 +import androidx.activity.result.component2 +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.LayoutRes import androidx.appcompat.app.AlertDialog import androidx.core.content.pm.ShortcutInfoCompat @@ -22,8 +24,6 @@ import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceFragmentCompat -import com.afollestad.materialdialogs.MaterialDialog -import com.afollestad.materialdialogs.input.input import com.github.shadowsocks.plugin.Empty import com.github.shadowsocks.plugin.fragment.AlertDialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -217,10 +217,6 @@ abstract class ProfileSettingsActivity( preferenceManager.preferenceDataStore = DataStore.profileCacheStore activity.apply { createPreferences(savedInstanceState, rootKey) - - if (isSubscription) { -// findPreference(Key.PROFILE_NAME)?.isEnabled = false - } } } @@ -237,6 +233,21 @@ abstract class ProfileSettingsActivity( DataStore.profileCacheStore.registerChangeListener(activity) } + var callbackCustom: ((String) -> Unit)? = null + var callbackCustomOutbound: ((String) -> Unit)? = null + + val resultCallbackCustom = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { (_, _) -> + callbackCustom?.let { it(DataStore.serverCustom) } + } + + val resultCallbackCustomOutbound = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { (_, _) -> + callbackCustomOutbound?.let { it(DataStore.serverCustomOutbound) } + } + @SuppressLint("CheckResult") override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { R.id.action_delete -> { @@ -263,36 +274,30 @@ abstract class ProfileSettingsActivity( R.id.action_custom_outbound_json -> { activity.proxyEntity?.apply { val bean = requireBean() - MaterialDialog(activity).show { - title(R.string.custom_outbound_json) - input( - prefill = bean.customOutboundJson, - inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE, - allowEmpty = true - ) { _, str -> - bean.customOutboundJson = str.toString() - DataStore.dirty = true - } - positiveButton(R.string.save) - } + DataStore.serverCustomOutbound = bean.customOutboundJson + callbackCustomOutbound = { bean.customOutboundJson = it } + resultCallbackCustomOutbound.launch( + Intent( + requireContext(), + ConfigEditActivity::class.java + ).apply { + putExtra("key", Key.SERVER_CUSTOM_OUTBOUND) + }) } true } R.id.action_custom_config_json -> { activity.proxyEntity?.apply { val bean = requireBean() - MaterialDialog(activity).show { - title(R.string.custom_config_json) - input( - prefill = bean.customConfigJson, - inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE, - allowEmpty = true - ) { _, str -> - bean.customConfigJson = str.toString() - DataStore.dirty = true - } - positiveButton(R.string.save) - } + DataStore.serverCustom = bean.customConfigJson + callbackCustom = { bean.customConfigJson = it } + resultCallbackCustom.launch( + Intent( + requireContext(), + ConfigEditActivity::class.java + ).apply { + putExtra("key", Key.SERVER_CUSTOM) + }) } true } diff --git a/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigSettingActivity.kt b/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigSettingActivity.kt index 8721579..7c0472b 100644 --- a/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigSettingActivity.kt +++ b/app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigSettingActivity.kt @@ -1,7 +1,6 @@ package moe.matsuri.nb4a.proxy.config import android.os.Bundle -import androidx.preference.EditTextPreference import androidx.preference.PreferenceDataStore import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference @@ -10,14 +9,13 @@ import io.nekohasekai.sagernet.R import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.preference.OnPreferenceDataStoreChangeListener import io.nekohasekai.sagernet.ui.profile.ProfileSettingsActivity +import moe.matsuri.nb4a.ui.EditConfigPreference class ConfigSettingActivity : ProfileSettingsActivity(), OnPreferenceDataStoreChangeListener { - var beanType: Int = 0 - - lateinit var configPreference: EditTextPreference + private var beanType: Int = 0 override fun createEntity() = ConfigBean() @@ -44,24 +42,30 @@ class ConfigSettingActivity : if (key != Key.PROFILE_DIRTY) { DataStore.dirty = true } - if (key == Key.SERVER_CONFIG) { - if (::configPreference.isInitialized) { - configPreference.text = store.getString(key, "") - } - } else if (key == "isOutboundOnly") { + if (key == "isOutboundOnly") { beanType = if (store.getBoolean(key, false)) 1 else 0 } } + private lateinit var editConfigPreference: EditConfigPreference + override fun PreferenceFragmentCompat.createPreferences( savedInstanceState: Bundle?, rootKey: String?, ) { addPreferencesFromResource(R.xml.config_preferences) - configPreference = findPreference(Key.SERVER_CONFIG)!! + editConfigPreference = findPreference(Key.SERVER_CONFIG)!! findPreference("isOutboundOnly")!!.isChecked = beanType == 1 } + override fun onResume() { + super.onResume() + + if (::editConfigPreference.isInitialized) { + editConfigPreference.notifyChanged() + } + } + } \ No newline at end of file diff --git a/app/src/main/java/moe/matsuri/nb4a/ui/EditConfigPreference.kt b/app/src/main/java/moe/matsuri/nb4a/ui/EditConfigPreference.kt new file mode 100644 index 0000000..6b2ea81 --- /dev/null +++ b/app/src/main/java/moe/matsuri/nb4a/ui/EditConfigPreference.kt @@ -0,0 +1,42 @@ +package moe.matsuri.nb4a.ui + +import android.content.Context +import android.content.Intent +import android.util.AttributeSet +import androidx.preference.Preference +import io.nekohasekai.sagernet.R +import io.nekohasekai.sagernet.database.DataStore +import io.nekohasekai.sagernet.ktx.app +import io.nekohasekai.sagernet.ui.profile.ConfigEditActivity + +class EditConfigPreference : Preference { + + constructor( + context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int + ) : super(context, attrs, defStyleAttr, defStyleRes) + + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( + context, attrs, defStyleAttr + ) + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context) : super(context) + + init { + intent = Intent(context, ConfigEditActivity::class.java) + } + + override fun getSummary(): CharSequence { + val config = DataStore.serverConfig + return if (DataStore.serverConfig.isBlank()) { + return app.resources.getString(androidx.preference.R.string.not_set) + } else { + app.resources.getString(R.string.lines, config.split('\n').size) + } + } + + public override fun notifyChanged() { + super.notifyChanged() + } + +} diff --git a/app/src/main/java/moe/matsuri/nb4a/ui/ExtendedKeyboard.kt b/app/src/main/java/moe/matsuri/nb4a/ui/ExtendedKeyboard.kt new file mode 100644 index 0000000..fabe5a0 --- /dev/null +++ b/app/src/main/java/moe/matsuri/nb4a/ui/ExtendedKeyboard.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2021 Squircle IDE contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package moe.matsuri.nb4a.ui + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import io.nekohasekai.sagernet.databinding.ItemKeyboardKeyBinding + +class ExtendedKeyboard @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RecyclerView(context, attrs, defStyleAttr) { + + private lateinit var keyAdapter: KeyAdapter + + fun setKeyListener(keyListener: OnKeyListener) { + keyAdapter = KeyAdapter(keyListener) + adapter = keyAdapter + } + + fun submitList(keys: List) { + keyAdapter.submitList(keys) + } + + private class KeyAdapter( + private val keyListener: OnKeyListener + ) : ListAdapter(diffCallback) { + + companion object { + private val diffCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: String, newItem: String): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: String, newItem: String): Boolean { + return oldItem == newItem + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): KeyViewHolder { + return KeyViewHolder.create(parent, keyListener) + } + + override fun onBindViewHolder(holder: KeyViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + private class KeyViewHolder( + private val binding: ItemKeyboardKeyBinding, + private val keyListener: OnKeyListener + ) : ViewHolder(binding.root) { + + companion object { + fun create(parent: ViewGroup, keyListener: OnKeyListener): KeyViewHolder { + val inflater = LayoutInflater.from(parent.context) + val binding = ItemKeyboardKeyBinding.inflate(inflater, parent, false) + return KeyViewHolder(binding, keyListener) + } + } + + private lateinit var char: String + + init { + itemView.setOnClickListener { + keyListener.onKey(char) + } + } + + fun bind(item: String) { + char = item + binding.itemTitle.text = char + binding.itemTitle.setTextColor(Color.WHITE) + } + } + } + + fun interface OnKeyListener { + fun onKey(char: String) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_keyboard_tab_24.xml b/app/src/main/res/drawable/baseline_keyboard_tab_24.xml new file mode 100644 index 0000000..3f7efd9 --- /dev/null +++ b/app/src/main/res/drawable/baseline_keyboard_tab_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_redo_24.xml b/app/src/main/res/drawable/baseline_redo_24.xml new file mode 100644 index 0000000..31c325f --- /dev/null +++ b/app/src/main/res/drawable/baseline_redo_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_undo_24.xml b/app/src/main/res/drawable/baseline_undo_24.xml new file mode 100644 index 0000000..5d17998 --- /dev/null +++ b/app/src/main/res/drawable/baseline_undo_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/item_keyboard_key.xml b/app/src/main/res/layout/item_keyboard_key.xml new file mode 100644 index 0000000..e88ac72 --- /dev/null +++ b/app/src/main/res/layout/item_keyboard_key.xml @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_edit_config.xml b/app/src/main/res/layout/layout_edit_config.xml new file mode 100644 index 0000000..7b22e21 --- /dev/null +++ b/app/src/main/res/layout/layout_edit_config.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/config_preferences.xml b/app/src/main/res/xml/config_preferences.xml index ec2b3e7..8866cd9 100644 --- a/app/src/main/res/xml/config_preferences.xml +++ b/app/src/main/res/xml/config_preferences.xml @@ -11,7 +11,7 @@ app:key="isOutboundOnly" app:title="@string/is_outbound_only" /> - /dev/null 2>&1 -mv preferencex-android-* preferencex - -rm tmp.zip +#rm -rf external +#mkdir -p external +#cd external +# +#echo "Downloading preferencex-android" +#wget -q -O tmp.zip https://github.com/SagerNet/preferencex-android/archive/8bdb0c6ae44f378b073c6a1c850d03d729b70ff8.zip +#unzip tmp.zip > /dev/null 2>&1 +#mv preferencex-android-* preferencex +# +#rm tmp.zip diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 12c940b..a6aa10a 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -7,6 +7,6 @@ apply(from = "../repositories.gradle.kts") dependencies { // Gradle Plugins - implementation("com.android.tools.build:gradle:7.3.1") - implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21") + implementation("com.android.tools.build:gradle:7.4.2") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10") } diff --git a/buildSrc/src/main/kotlin/Helpers.kt b/buildSrc/src/main/kotlin/Helpers.kt index 432f437..9d1801f 100644 --- a/buildSrc/src/main/kotlin/Helpers.kt +++ b/buildSrc/src/main/kotlin/Helpers.kt @@ -1,8 +1,11 @@ import com.android.build.gradle.AbstractAppExtension import com.android.build.gradle.BaseExtension import com.android.build.gradle.internal.api.BaseVariantOutputImpl +import org.gradle.api.JavaVersion import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.getByName +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import java.security.MessageDigest import java.util.* import kotlin.system.exitProcess @@ -96,6 +99,13 @@ fun Project.setupCommon() { isMinifyEnabled = true } } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + (android as ExtensionAware).extensions.getByName("kotlinOptions").apply { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } lintOptions { isShowAll = true isCheckAllWarnings = true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cade3fa..7e62b17 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME