1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.virtualization.terminal 17 18 import android.content.DialogInterface 19 import android.os.Bundle 20 import android.text.Editable 21 import android.text.TextWatcher 22 import android.widget.EditText 23 import android.widget.ImageButton 24 import androidx.appcompat.app.AlertDialog 25 import androidx.appcompat.app.AppCompatActivity 26 import androidx.recyclerview.widget.LinearLayoutManager 27 import androidx.recyclerview.widget.RecyclerView 28 import com.google.android.material.dialog.MaterialAlertDialogBuilder 29 30 private const val PORT_RANGE_MIN: Int = 1024 31 private const val PORT_RANGE_MAX: Int = 65535 32 33 class SettingsPortForwardingActivity : AppCompatActivity() { 34 private lateinit var portsStateManager: PortsStateManager 35 private lateinit var portsStateListener: Listener 36 private lateinit var activePortsAdapter: SettingsPortForwardingActiveAdapter 37 private lateinit var inactivePortsAdapter: SettingsPortForwardingInactiveAdapter 38 39 override fun onCreate(savedInstanceState: Bundle?) { 40 super.onCreate(savedInstanceState) 41 setContentView(R.layout.settings_port_forwarding) 42 43 portsStateManager = PortsStateManager.getInstance(this) 44 45 activePortsAdapter = SettingsPortForwardingActiveAdapter(portsStateManager, this) 46 val activeRecyclerView: RecyclerView = 47 findViewById(R.id.settings_port_forwarding_active_recycler_view) 48 activeRecyclerView.layoutManager = LinearLayoutManager(this) 49 activeRecyclerView.adapter = activePortsAdapter 50 51 inactivePortsAdapter = SettingsPortForwardingInactiveAdapter(portsStateManager, this) 52 val inactiveRecyclerView: RecyclerView = 53 findViewById(R.id.settings_port_forwarding_inactive_recycler_view) 54 inactiveRecyclerView.layoutManager = LinearLayoutManager(this) 55 inactiveRecyclerView.adapter = inactivePortsAdapter 56 57 portsStateListener = Listener() 58 59 val addButton = findViewById<ImageButton>(R.id.settings_port_forwarding_inactive_add_button) 60 addButton.setOnClickListener { 61 val dialog = 62 MaterialAlertDialogBuilder(this) 63 .setTitle(R.string.settings_port_forwarding_dialog_title) 64 .setView(R.layout.settings_port_forwarding_inactive_add_dialog) 65 .setPositiveButton(R.string.settings_port_forwarding_dialog_save) { 66 dialogInterface, 67 _ -> 68 val alertDialog = dialogInterface as AlertDialog 69 val editText = 70 alertDialog.findViewById<EditText>( 71 R.id.settings_port_forwarding_inactive_add_dialog_text 72 )!! 73 val port = editText.text.toString().toInt() 74 portsStateManager.updateEnabledPort(port, true) 75 } 76 .setNegativeButton(R.string.settings_port_forwarding_dialog_cancel, null) 77 .create() 78 dialog.show() 79 80 val positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE) 81 positiveButton.setEnabled(false) 82 val editText = 83 dialog.findViewById<EditText>( 84 R.id.settings_port_forwarding_inactive_add_dialog_text 85 )!! 86 editText.addTextChangedListener( 87 object : TextWatcher { 88 override fun beforeTextChanged( 89 s: CharSequence?, 90 start: Int, 91 count: Int, 92 after: Int, 93 ) {} 94 95 override fun afterTextChanged(s: Editable?) {} 96 97 override fun onTextChanged( 98 s: CharSequence?, 99 start: Int, 100 before: Int, 101 count: Int, 102 ) { 103 val port = 104 try { 105 s.toString().toInt() 106 } catch (e: NumberFormatException) { 107 editText.setError( 108 getString( 109 R.string.settings_port_forwarding_dialog_error_invalid_input 110 ) 111 ) 112 positiveButton.setEnabled(false) 113 return@onTextChanged 114 } 115 if (port > PORT_RANGE_MAX || port < PORT_RANGE_MIN) { 116 editText.setError( 117 getString( 118 R.string 119 .settings_port_forwarding_dialog_error_invalid_port_range 120 ) 121 ) 122 positiveButton.setEnabled(false) 123 } else if ( 124 portsStateManager.getActivePorts().contains(port) || 125 portsStateManager.getEnabledPorts().contains(port) 126 ) { 127 editText.setError( 128 getString( 129 R.string.settings_port_forwarding_dialog_error_existing_port 130 ) 131 ) 132 positiveButton.setEnabled(false) 133 } else { 134 positiveButton.setEnabled(true) 135 } 136 } 137 } 138 ) 139 } 140 } 141 142 private fun refreshAdapters() { 143 runOnUiThread { 144 activePortsAdapter.refreshItems() 145 inactivePortsAdapter.refreshItems() 146 } 147 } 148 149 override fun onResume() { 150 super.onResume() 151 portsStateManager.registerListener(portsStateListener) 152 refreshAdapters() 153 } 154 155 override fun onPause() { 156 portsStateManager.unregisterListener(portsStateListener) 157 super.onPause() 158 } 159 160 private inner class Listener : PortsStateManager.Listener { 161 override fun onPortsStateUpdated(oldActivePorts: Set<Int>, newActivePorts: Set<Int>) { 162 refreshAdapters() 163 } 164 } 165 } 166