Add mock settings pages to terminal app
This commit adds three settings pages to the terminal app:
1. Disk Resize
2. Port Forwarding
3. Recovery
To enable the adaptive layout for mobile and large screen devices for
these settings pages, this commit introduces activity embedding layout.
Bug: 364149286
Test: install on tangorpro and komodo
Change-Id: Iccbd1578b0d729e7e295d0533e4db54c6fd1d815
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 1a7c581..d91af2f 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -4,10 +4,16 @@
android_app {
name: "VmTerminalApp",
- srcs: ["java/**/*.java"],
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
resource_dirs: ["res"],
static_libs: [
"vm_launcher_lib",
+ "androidx-constraintlayout_constraintlayout",
+ "com.google.android.material_material",
+ "androidx.window_window",
],
platform_apis: true,
privileged: true,
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index e338c49..f09412e 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.virtualization.terminal" >
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.virtualization.terminal">
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
@@ -9,9 +10,11 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-feature android:name="android.software.virtualization_framework" android:required="true" />
+
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
+ android:theme="@style/Theme.Material3.DayNight.NoActionBar"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode|screenLayout|smallestScreenSize"
@@ -21,6 +24,26 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name=".SettingsActivity">
+ </activity>
+ <activity android:name=".SettingsDiskResizeActivity">
+ </activity>
+ <activity android:name=".SettingsPortForwardingActivity">
+ </activity>
+ <activity android:name=".SettingsRecoveryActivity">
+ </activity>
+ <property
+ android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
+ android:value="true" />
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ android:exported="false"
+ tools:node="merge">
+ <meta-data
+ android:name="${applicationId}.SplitInitializer"
+ android:value="androidx.startup" />
+ </provider>
<activity-alias
android:name=".MainActivityAlias"
android:targetActivity="com.android.virtualization.terminal.MainActivity"
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index a6723fb..846f975 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -15,9 +15,9 @@
*/
package com.android.virtualization.terminal;
-import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -30,9 +30,14 @@
import android.widget.TextView;
import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+
import com.android.virtualization.vmlauncher.VmLauncherServices;
-public class MainActivity extends Activity implements VmLauncherServices.VmLauncherServiceCallback {
+import com.google.android.material.appbar.MaterialToolbar;
+
+public class MainActivity extends AppCompatActivity implements
+ VmLauncherServices.VmLauncherServiceCallback {
private static final String TAG = "VmTerminalApp";
private String mVmIpAddr;
private WebView mWebView;
@@ -44,6 +49,9 @@
VmLauncherServices.startVmLauncherService(this, this);
setContentView(R.layout.activity_headless);
+
+ MaterialToolbar toolbar = (MaterialToolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setDatabaseEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
@@ -101,7 +109,7 @@
}
@Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.copy_ip_addr) {
// TODO(b/340126051): remove this menu item when port forwarding is supported.
@@ -111,7 +119,12 @@
} else if (id == R.id.stop_vm) {
VmLauncherServices.stopVmLauncherService(this);
return true;
+
+ } else if (id == R.id.menu_item_settings) {
+ Intent intent = new Intent(this, SettingsActivity.class);
+ this.startActivity(intent);
+ return true;
}
- return super.onMenuItemSelected(featureId, item);
+ return super.onOptionsItemSelected(item);
}
}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
new file mode 100644
index 0000000..dccfea3
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+class SettingsActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_activity)
+
+ val settingsItems = arrayOf(
+ SettingsItem(
+ resources.getString(R.string.settings_disk_resize_title),
+ resources.getString(R.string.settings_disk_resize_sub_title),
+ R.drawable.baseline_storage_24,
+ SettingsItemEnum.DiskResize
+ ),
+ SettingsItem(
+ resources.getString(R.string.settings_port_forwarding_title),
+ resources.getString(R.string.settings_port_forwarding_sub_title),
+ R.drawable.baseline_call_missed_outgoing_24,
+ SettingsItemEnum.PortForwarding
+ ),
+ SettingsItem(
+ resources.getString(R.string.settings_recovery_title),
+ resources.getString(R.string.settings_recovery_sub_title),
+ R.drawable.baseline_settings_backup_restore_24,
+ SettingsItemEnum.Recovery
+ ),
+ )
+ val settingsListItemAdapter = SettingsItemAdapter(settingsItems)
+
+ val recyclerView: RecyclerView = findViewById(R.id.settings_list_recycler_view)
+ recyclerView.layoutManager = LinearLayoutManager(this)
+ recyclerView.adapter = settingsListItemAdapter
+ }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
new file mode 100644
index 0000000..4be291f
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.os.Bundle
+import android.widget.TextView
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.isVisible
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.slider.Slider
+
+class SettingsDiskResizeActivity : AppCompatActivity() {
+ private var diskSize: Float = 104F
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_disk_resize)
+ val diskSizeText = findViewById<TextView>(R.id.settings_disk_resize_disk_size)
+ val diskSizeSlider = findViewById<Slider>(R.id.settings_disk_resize_disk_size_slider)
+ val cancelButton = findViewById<MaterialButton>(R.id.settings_disk_resize_cancel_button)
+ val resizeButton = findViewById<MaterialButton>(R.id.settings_disk_resize_resize_button)
+ diskSizeText.text = diskSize.toInt().toString()
+ diskSizeSlider.value = diskSize
+
+ diskSizeSlider.addOnChangeListener { _, value, _ ->
+ diskSizeText.text = value.toInt().toString()
+ cancelButton.isVisible = true
+ resizeButton.isVisible = true
+ }
+ cancelButton.setOnClickListener {
+ diskSizeSlider.value = diskSize
+ cancelButton.isVisible = false
+ resizeButton.isVisible = false
+ }
+
+ resizeButton.setOnClickListener {
+ diskSize = diskSizeSlider.value
+ cancelButton.isVisible = false
+ resizeButton.isVisible = false
+ Toast.makeText(this@SettingsDiskResizeActivity, R.string.settings_disk_resize_resize_message, Toast.LENGTH_SHORT)
+ .show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
new file mode 100644
index 0000000..e1723a7
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItem.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+enum class SettingsItemEnum {
+ DiskResize, PortForwarding, Recovery
+}
+
+class SettingsItem(
+ val title: String,
+ val subTitle: String,
+ val icon: Int,
+ val settingsItemEnum: SettingsItemEnum
+) {
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
new file mode 100644
index 0000000..86f5c92
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.content.Intent
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.card.MaterialCardView
+
+class SettingsItemAdapter(private val dataSet: Array<SettingsItem>) :
+ RecyclerView.Adapter<SettingsItemAdapter.ViewHolder>() {
+
+ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val card: MaterialCardView = view.findViewById(R.id.settings_list_item_card)
+ val icon: ImageView = view.findViewById(R.id.settings_list_item_icon)
+ val title: TextView = view.findViewById(R.id.settings_list_item_title)
+ val subTitle: TextView = view.findViewById(R.id.settings_list_item_sub_title)
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(viewGroup.context)
+ .inflate(R.layout.settings_list_item, viewGroup, false)
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
+ viewHolder.icon.setImageResource(dataSet[position].icon)
+ viewHolder.title.text = dataSet[position].title
+ viewHolder.subTitle.text = dataSet[position].subTitle
+
+ viewHolder.card.setOnClickListener { view ->
+ val intent = Intent(
+ viewHolder.itemView.context,
+ when (dataSet[position].settingsItemEnum) {
+ SettingsItemEnum.DiskResize -> SettingsDiskResizeActivity::class.java
+ SettingsItemEnum.PortForwarding -> SettingsPortForwardingActivity::class.java
+ SettingsItemEnum.Recovery -> SettingsRecoveryActivity::class.java
+ }
+ )
+ view.context.startActivity(intent)
+ }
+ }
+
+ override fun getItemCount() = dataSet.size
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
new file mode 100644
index 0000000..6c36cc8
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingActivity.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+
+class SettingsPortForwardingActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_port_forwarding)
+
+ val settingsPortForwardingItems = arrayOf(
+ SettingsPortForwardingItem(8080, true),
+ SettingsPortForwardingItem(443, false),
+ SettingsPortForwardingItem(80, false)
+ )
+
+ val settingsPortForwardingAdapter =
+ SettingsPortForwardingAdapter(settingsPortForwardingItems)
+
+ val recyclerView: RecyclerView = findViewById(R.id.settings_port_forwarding_recycler_view)
+ recyclerView.layoutManager = LinearLayoutManager(this)
+ recyclerView.adapter = settingsPortForwardingAdapter
+ }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
new file mode 100644
index 0000000..1fa38e3
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingAdapter.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.material.materialswitch.MaterialSwitch
+
+class SettingsPortForwardingAdapter(private val dataSet: Array<SettingsPortForwardingItem>) :
+ RecyclerView.Adapter<SettingsPortForwardingAdapter.ViewHolder>() {
+
+ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val enabledSwitch: MaterialSwitch =
+ view.findViewById(R.id.settings_port_forwarding_item_enabled_switch)
+ val port: TextView = view.findViewById(R.id.settings_port_forwarding_item_port)
+ }
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(viewGroup.context)
+ .inflate(R.layout.settings_port_forwarding_item, viewGroup, false)
+ return ViewHolder(view)
+ }
+
+ override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
+ viewHolder.port.text = dataSet[position].port.toString()
+ viewHolder.enabledSwitch.isChecked = dataSet[position].enabled
+ }
+
+ override fun getItemCount() = dataSet.size
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt
new file mode 100644
index 0000000..599e377
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsPortForwardingItem.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+class SettingsPortForwardingItem(val port: Int, val enabled: Boolean) {}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
new file mode 100644
index 0000000..7256015
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsRecoveryActivity.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.os.Bundle
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import com.google.android.material.card.MaterialCardView
+
+class SettingsRecoveryActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_recovery)
+ val resetCard = findViewById<MaterialCardView>(R.id.settings_recovery_reset_card)
+ resetCard.setOnClickListener {
+ Toast.makeText(this@SettingsRecoveryActivity, R.string.settings_recovery_reset_message, Toast.LENGTH_SHORT).show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
new file mode 100644
index 0000000..cb917bd
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * 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 com.android.virtualization.terminal
+
+import android.content.Context
+import androidx.startup.Initializer
+import androidx.window.embedding.RuleController
+
+class SplitInitializer : Initializer<RuleController> {
+
+ override fun create(context: Context): RuleController {
+ return RuleController.getInstance(context).apply {
+ setRules(RuleController.parseRules(context, R.xml.main_split_config))
+ }
+ }
+
+ override fun dependencies(): List<Class<out Initializer<*>>> {
+ return emptyList()
+ }
+}
\ No newline at end of file
diff --git a/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
new file mode 100644
index 0000000..597c317
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_call_missed_outgoing_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M3,8.41l9,9l7,-7V15h2V7h-8v2h4.59L12,14.59L4.41,7L3,8.41z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
new file mode 100644
index 0000000..22b23ba
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_settings_backup_restore_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/drawable/baseline_storage_24.xml b/android/TerminalApp/res/drawable/baseline_storage_24.xml
new file mode 100644
index 0000000..6e52e3f
--- /dev/null
+++ b/android/TerminalApp/res/drawable/baseline_storage_24.xml
@@ -0,0 +1,5 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
+
+ <path android:fillColor="@android:color/white" android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/>
+
+</vector>
diff --git a/android/TerminalApp/res/layout/activity_headless.xml b/android/TerminalApp/res/layout/activity_headless.xml
index 3fe5271..f786a0f 100644
--- a/android/TerminalApp/res/layout/activity_headless.xml
+++ b/android/TerminalApp/res/layout/activity_headless.xml
@@ -7,6 +7,11 @@
android:orientation="vertical"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
+ <com.google.android.material.appbar.MaterialToolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/ip_addr_textview"
android:layout_width="wrap_content"
diff --git a/android/TerminalApp/res/layout/settings_activity.xml b/android/TerminalApp/res/layout/settings_activity.xml
new file mode 100644
index 0000000..b1acf23
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_activity.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
+
+ <com.google.android.material.search.SearchBar
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/settings_list_recycler_view"
+ android:layout_marginHorizontal="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_disk_resize.xml b/android/TerminalApp/res/layout/settings_disk_resize.xml
new file mode 100644
index 0000000..3c09f52
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_disk_resize.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
+ android:layout_marginTop="24dp"
+ android:fitsSystemWindows="true">
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/settings_disk_resize_title"
+ android:textSize="48sp"
+ android:layout_marginBottom="24dp"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/settings_disk_resize_disk_size"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:textSize="36sp"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/settings_disk_resize_resize_gb_assigned"
+ android:textSize="14sp"
+ app:layout_constraintLeft_toRightOf="@+id/settings_disk_resize_disk_size"
+ app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/settings_disk_resize_resize_gb_total"
+ android:textSize="14sp"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/settings_disk_resize_disk_size_slider"/>
+
+ <com.google.android.material.slider.Slider
+ android:id="@+id/settings_disk_resize_disk_size_slider"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="36dp"
+ app:tickVisible="false"
+ android:valueFrom="0"
+ android:valueTo="256"
+ android:stepSize="4"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/settings_disk_resize_cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/settings_disk_resize_resize_cancel"
+ android:visibility="invisible"
+ android:layout_marginHorizontal="8dp"
+ app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintRight_toLeftOf="@+id/settings_disk_resize_resize_button" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/settings_disk_resize_resize_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/settings_disk_resize_resize_restart_vm_to_apply"
+ android:visibility="invisible"
+ android:layout_marginHorizontal="8dp"
+ app:layout_constraintTop_toTopOf="@+id/settings_disk_resize_disk_size_slider"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintRight_toRightOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_list_item.xml b/android/TerminalApp/res/layout/settings_list_item.xml
new file mode 100644
index 0000000..89f2d82
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_list_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:gravity="center_vertical"
+ android:layout_height="wrap_content">
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/settings_list_item_card"
+ app:strokeWidth="0dp"
+ app:cardCornerRadius="28dp"
+ app:checkedIcon="@null"
+ android:focusable="true"
+ android:checkable="true"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="16dp">
+
+ <com.google.android.material.imageview.ShapeableImageView
+ android:id="@+id/settings_list_item_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="24dp"
+ android:scaleType="centerCrop"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent" />
+
+ <TextView
+ android:id="@+id/settings_list_item_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:layout_marginStart="24dp"
+ android:textSize="20sp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/settings_list_item_sub_title"
+ app:layout_constraintStart_toEndOf="@id/settings_list_item_icon"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <TextView
+ android:id="@+id/settings_list_item_sub_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginStart="24dp"
+ app:layout_constraintTop_toBottomOf="@+id/settings_list_item_title"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/settings_list_item_icon"
+ app:layout_constraintEnd_toEndOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.google.android.material.card.MaterialCardView>
+</FrameLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding.xml b/android/TerminalApp/res/layout/settings_port_forwarding.xml
new file mode 100644
index 0000000..1d68907
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_port_forwarding.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
+ android:layout_marginTop="24dp"
+ android:fitsSystemWindows="true">
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/settings_port_forwarding_title"
+ android:textSize="48sp"
+ android:layout_marginBottom="24dp"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/settings_port_forwarding_recycler_view"
+ android:layout_marginHorizontal="16dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_port_forwarding_item.xml b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
new file mode 100644
index 0000000..9e5981e
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_port_forwarding_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintCircleRadius="@dimen/material_emphasis_medium"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <TextView
+ android:id="@+id/settings_port_forwarding_item_port"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"/>
+
+ <com.google.android.material.materialswitch.MaterialSwitch
+ android:id="@+id/settings_port_forwarding_item_enabled_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintRight_toRightOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/settings_recovery.xml b/android/TerminalApp/res/layout/settings_recovery.xml
new file mode 100644
index 0000000..e18c8a6
--- /dev/null
+++ b/android/TerminalApp/res/layout/settings_recovery.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_marginEnd="24dp"
+ android:layout_marginTop="24dp"
+ android:fitsSystemWindows="true">
+
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/settings_recovery_title"
+ android:textSize="48sp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="24dp"/>
+
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/settings_recovery_reset_card"
+ app:strokeWidth="0dp"
+ app:cardCornerRadius="0dp"
+ app:checkedIcon="@null"
+ android:focusable="true"
+ android:checkable="true"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginStart="24dp">
+
+ <TextView
+ android:id="@+id/settings_recovery_reset_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:layout_marginStart="24dp"
+ android:textSize="20sp"
+ android:text="@string/settings_recovery_reset_title"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/settings_recovery_reset_sub_title"
+ app:layout_constraintLeft_toLeftOf="parent" />
+
+ <TextView
+ android:id="@+id/settings_recovery_reset_sub_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:layout_marginBottom="20dp"
+ android:layout_marginStart="24dp"
+ android:text="@string/settings_recovery_reset_sub_title"
+ app:layout_constraintTop_toBottomOf="@+id/settings_recovery_reset_title"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.google.android.material.card.MaterialCardView>
+</LinearLayout>
\ No newline at end of file
diff --git a/android/TerminalApp/res/menu/main_menu.xml b/android/TerminalApp/res/menu/main_menu.xml
index cc34cda..9c83923 100644
--- a/android/TerminalApp/res/menu/main_menu.xml
+++ b/android/TerminalApp/res/menu/main_menu.xml
@@ -4,4 +4,6 @@
android:title="Copy the IP address"/>
<item android:id="@+id/stop_vm"
android:title="Stop the existing VM instance"/>
+ <item android:id="@+id/menu_item_settings"
+ android:title="Settings"/>
</menu>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/dimens.xml b/android/TerminalApp/res/values/dimens.xml
new file mode 100644
index 0000000..e6ed461
--- /dev/null
+++ b/android/TerminalApp/res/values/dimens.xml
@@ -0,0 +1,3 @@
+<resources>
+ <dimen name="activity_split_ratio">0.3</dimen>
+</resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/integers.xml b/android/TerminalApp/res/values/integers.xml
new file mode 100644
index 0000000..0c7d2b9
--- /dev/null
+++ b/android/TerminalApp/res/values/integers.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="split_min_width">720</integer>
+</resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index 79da7cd..c3a3348 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -20,4 +20,21 @@
<string name="vm_creation_message">Virtual machine is booting. Please wait.</string>
<string name="vm_stop_message">Virtual machine is stopped. Exiting.</string>
<string name="vm_error_message">Virtual machine crashed. Exiting.</string>
+
+ <string name="settings_disk_resize_title">Disk Resize</string>
+ <string name="settings_disk_resize_sub_title">Resize / Rootfs</string>
+ <string name="settings_disk_resize_resize_message">Disk size set</string>
+ <string name="settings_disk_resize_resize_gb_assigned">GB Assigned</string>
+ <string name="settings_disk_resize_resize_gb_total">256 GB total</string>
+ <string name="settings_disk_resize_resize_cancel">Cancel</string>
+ <string name="settings_disk_resize_resize_restart_vm_to_apply">Restart VM to apply</string>
+
+ <string name="settings_port_forwarding_title">Port Forwarding</string>
+ <string name="settings_port_forwarding_sub_title">Configure port forwarding</string>
+
+ <string name="settings_recovery_title">Recovery</string>
+ <string name="settings_recovery_sub_title">Partition Recovery options</string>
+ <string name="settings_recovery_reset_title">Change to Initial version</string>
+ <string name="settings_recovery_reset_sub_title">Remove all</string>
+ <string name="settings_recovery_reset_message">VM reset</string>
</resources>
diff --git a/android/TerminalApp/res/xml/main_split_config.xml b/android/TerminalApp/res/xml/main_split_config.xml
new file mode 100644
index 0000000..f51e7ea
--- /dev/null
+++ b/android/TerminalApp/res/xml/main_split_config.xml
@@ -0,0 +1,37 @@
+<resources xmlns:window="http://schemas.android.com/apk/res-auto">
+
+ <!-- Define a split for the named activities. -->
+ <ActivityRule window:alwaysExpand="true">
+ <ActivityFilter window:activityName=".MainActivity" />
+ </ActivityRule>
+
+ <SplitPairRule
+ window:clearTop="true"
+ window:finishPrimaryWithSecondary="adjacent"
+ window:finishSecondaryWithPrimary="always"
+ window:splitLayoutDirection="locale"
+ window:splitMaxAspectRatioInPortrait="alwaysAllow"
+ window:splitMinWidthDp="@integer/split_min_width"
+ window:splitRatio="@dimen/activity_split_ratio">
+ <SplitPairFilter
+ window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+ window:secondaryActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity" />
+ <SplitPairFilter
+ window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+ window:secondaryActivityName="com.android.virtualization.terminal.SettingsPortForwardingActivity" />
+ <SplitPairFilter
+ window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
+ window:secondaryActivityName="com.android.virtualization.terminal.SettingsRecoveryActivity" />
+ </SplitPairRule>
+
+ <SplitPlaceholderRule
+ window:placeholderActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity"
+ window:splitLayoutDirection="locale"
+ window:splitMaxAspectRatioInPortrait="alwaysAllow"
+ window:splitMinWidthDp="@integer/split_min_width"
+ window:splitRatio="@dimen/activity_split_ratio">
+ window:stickyPlaceholder="false">
+ <ActivityFilter
+ window:activityName="com.android.virtualization.terminal.SettingsActivity"/>
+ </SplitPlaceholderRule>
+</resources>
\ No newline at end of file