• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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