Skip to content

Commit 406ed05

Browse files
committed
feat(ui): migrate screens to MikuRay layout
1 parent 755f452 commit 406ed05

166 files changed

Lines changed: 2623 additions & 531 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/build.gradle.kts

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
import java.io.File
23
import java.util.Properties
34
import java.util.zip.ZipEntry
45
import java.util.zip.ZipFile
@@ -15,7 +16,7 @@ val localProps = Properties().apply {
1516
}
1617

1718
fun secret(name: String): String? =
18-
(findProperty(name) as String?)?.takeIf { it.isNotBlank() }
19+
(findProperty(name) as? String)?.takeIf { it.isNotBlank() }
1920
?: localProps.getProperty(name)?.takeIf { it.isNotBlank() }
2021
?: System.getenv(name)?.takeIf { it.isNotBlank() }
2122

@@ -69,11 +70,10 @@ android {
6970

7071
packaging {
7172
resources {
72-
// Coroutine debug probes are useful only to a debugger and should
73-
// not be packaged in production or debug APKs.
74-
excludes += "DebugProbesKt.bin"
75-
excludes += "META-INF/**"
76-
excludes += "/META-INF/**"
73+
excludes += listOf(
74+
"DebugProbesKt.bin",
75+
"META-INF/**"
76+
)
7777
}
7878
jniLibs {
7979
useLegacyPackaging = true
@@ -92,7 +92,7 @@ android {
9292
isMinifyEnabled = false
9393
proguardFiles(
9494
getDefaultProguardFile("proguard-android-optimize.txt"),
95-
"proguard-rules.pro",
95+
"proguard-rules.pro"
9696
)
9797
signingConfigs.findByName("release")?.let { signingConfig = it }
9898
}
@@ -121,41 +121,42 @@ val buildMihomoBridge by tasks.registering {
121121
outputs.dir(mihomoJniLibsDir)
122122

123123
doLast {
124-
val ndk = android.ndkDirectory
125-
// Pick the NDK prebuilt toolchain for the build host (Windows uses .cmd
126-
// wrappers) so this works both locally and on Linux/macOS CI runners.
124+
val ndkDir = android.ndkDirectory
127125
val hostOs = System.getProperty("os.name").lowercase()
128126
val (hostTag, exeExt) = when {
129127
hostOs.contains("win") -> "windows-x86_64" to ".cmd"
130128
hostOs.contains("mac") || hostOs.contains("darwin") -> "darwin-x86_64" to ""
131129
else -> "linux-x86_64" to ""
132130
}
133-
val clangDir = ndk.resolve("toolchains/llvm/prebuilt/$hostTag/bin")
131+
val clangDir = ndkDir.resolve("toolchains/llvm/prebuilt/$hostTag/bin")
134132
val targets = mapOf(
135133
"armeabi-v7a" to "armv7a-linux-androideabi24-clang",
136134
"arm64-v8a" to "aarch64-linux-android24-clang",
137-
"x86_64" to "x86_64-linux-android24-clang",
135+
"x86_64" to "x86_64-linux-android24-clang"
138136
)
139137

140138
targets.forEach { (abi, compiler) ->
141139
val output = mihomoJniLibsDir.get().file("$abi/libmihomo.so").asFile
142140
output.parentFile.mkdirs()
141+
142+
val goArch = when (abi) {
143+
"armeabi-v7a" -> "arm"
144+
"arm64-v8a" -> "arm64"
145+
"x86_64" -> "amd64"
146+
else -> error("Unsupported Android ABI: $abi")
147+
}
148+
143149
exec {
144150
workingDir = mihomoBridgeDir
145151
environment("GOOS", "android")
146-
environment("GOARCH", when (abi) {
147-
"armeabi-v7a" -> "arm"
148-
"arm64-v8a" -> "arm64"
149-
"x86" -> "386"
150-
"x86_64" -> "amd64"
151-
else -> error("Unsupported Android ABI: $abi")
152-
})
153-
environment("GOARM", if (abi == "armeabi-v7a") "7" else "")
152+
environment("GOARCH", goArch)
153+
if (abi == "armeabi-v7a") environment("GOARM", "7")
154154
environment("CGO_ENABLED", "1")
155155
environment("CC", clangDir.resolve(compiler + exeExt).absolutePath)
156+
156157
commandLine(
157158
"go", "build", "-trimpath", "-buildmode=c-shared",
158-
"-ldflags=-s -w", "-o", output.absolutePath, ".",
159+
"-ldflags=-s -w", "-o", output.absolutePath, "."
159160
)
160161
}
161162
}
@@ -179,8 +180,9 @@ val stripDebugApkMetadata by tasks.registering {
179180
val releaseAlias = secret("ALIAS_NAME")
180181
val releaseKeyPass = secret("ALIAS_PASS")
181182
val isWindows = System.getProperty("os.name").lowercase().contains("win")
182-
val executableSuffix = if (isWindows) ".exe" else ""
183-
val keytool = File(System.getProperty("java.home"), "bin/keytool$executableSuffix")
183+
val exeExt = if (isWindows) ".exe" else ""
184+
val keytool = File(System.getProperty("java.home"), "bin/keytool$exeExt")
185+
184186
val useReleaseKey = releaseKeystore.isFile &&
185187
releaseStorePass != null && releaseAlias != null && releaseKeyPass != null
186188
if (!useReleaseKey && !debugKeystore.isFile) {
@@ -198,7 +200,7 @@ val stripDebugApkMetadata by tasks.registering {
198200
"-keysize", "2048",
199201
"-validity", "10000",
200202
"-dname", "CN=Android Debug,O=Android,C=US",
201-
"-noprompt",
203+
"-noprompt"
202204
)
203205
}
204206
}
@@ -208,7 +210,7 @@ val stripDebugApkMetadata by tasks.registering {
208210
val signingKeyPass = if (useReleaseKey) releaseKeyPass!! else "android"
209211

210212
val buildToolsDir = android.sdkDirectory.resolve("build-tools/${android.buildToolsVersion}")
211-
val zipalign = buildToolsDir.resolve("zipalign$executableSuffix")
213+
val zipalign = buildToolsDir.resolve("zipalign$exeExt")
212214
val apksigner = buildToolsDir.resolve("apksigner${if (isWindows) ".bat" else ""}")
213215
check(zipalign.isFile && apksigner.isFile) { "Android build-tools are incomplete in $buildToolsDir" }
214216

@@ -221,26 +223,40 @@ val stripDebugApkMetadata by tasks.registering {
221223
while (entries.hasMoreElements()) {
222224
val entry = entries.nextElement()
223225
if (entry.name == "DebugProbesKt.bin" || entry.name.startsWith("META-INF/")) continue
224-
output.putNextEntry(ZipEntry(entry.name).apply { time = entry.time })
225-
if (!entry.isDirectory) input.getInputStream(entry).use { it.copyTo(output) }
226+
227+
val newEntry = ZipEntry(entry.name).apply {
228+
time = entry.time
229+
method = entry.method
230+
if (entry.method == ZipEntry.STORED) {
231+
size = entry.size
232+
compressedSize = entry.compressedSize
233+
crc = entry.crc
234+
}
235+
}
236+
237+
output.putNextEntry(newEntry)
238+
if (!entry.isDirectory) {
239+
input.getInputStream(entry).use { it.copyTo(output) }
240+
}
226241
output.closeEntry()
227242
}
228243
}
229244
}
245+
230246
exec { commandLine(zipalign.absolutePath, "-f", "4", unaligned.absolutePath, aligned.absolutePath) }
231247
exec {
232248
commandLine(
233-
apksigner.absolutePath,
234-
"sign",
249+
apksigner.absolutePath, "sign",
235250
"--ks", signingKeystore.absolutePath,
236251
"--ks-key-alias", signingAlias,
237252
"--ks-pass", "pass:$signingStorePass",
238253
"--key-pass", "pass:$signingKeyPass",
239254
"--v1-signing-enabled", "false",
240255
"--out", apk.absolutePath,
241-
aligned.absolutePath,
256+
aligned.absolutePath
242257
)
243258
}
259+
244260
unaligned.delete()
245261
aligned.delete()
246262
}

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@
2323
android:theme="@style/Theme.MikuBoxCla">
2424

2525
<activity
26-
android:name=".ui.MainActivity"
26+
android:name=".ui.SplashActivity"
2727
android:exported="true">
2828
<intent-filter>
2929
<action android:name="android.intent.action.MAIN" />
3030
<category android:name="android.intent.category.LAUNCHER" />
3131
</intent-filter>
3232
</activity>
3333

34+
<activity
35+
android:name=".ui.MainActivity"
36+
android:exported="false" />
37+
3438
<activity
3539
android:name=".ui.AboutActivity"
3640
android:exported="false"

app/src/main/java/top/uwu/mikubox/ui/MainActivity.kt

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ import android.os.Bundle
1010
import android.os.Handler
1111
import android.os.Looper
1212
import android.view.View
13-
import androidx.activity.OnBackPressedCallback
14-
import androidx.drawerlayout.widget.DrawerLayout
1513
import android.widget.Toast
14+
import androidx.appcompat.widget.PopupMenu
1615
import androidx.appcompat.app.AppCompatActivity
1716
import androidx.core.content.ContextCompat
18-
import androidx.core.view.GravityCompat
1917
import androidx.lifecycle.lifecycleScope
2018
import androidx.recyclerview.widget.LinearLayoutManager
2119
import kotlinx.coroutines.Dispatchers
@@ -68,42 +66,16 @@ class MainActivity : AppCompatActivity(), AddProfileBottomSheet.Listener {
6866
binding.rvProfiles.adapter = adapter
6967
binding.groupTab.addTab(binding.groupTab.newTab().setText(getString(R.string.profiles_header)))
7068

71-
binding.toolbar.setOnMenuItemClickListener { item ->
72-
if (item.itemId != R.id.action_add_profile) return@setOnMenuItemClickListener false
69+
binding.btnAddConfig.setOnClickListener {
7370
AddProfileBottomSheet().show(supportFragmentManager, AddProfileBottomSheet.TAG)
74-
true
7571
}
72+
binding.btnAddProfile.setOnClickListener {
73+
AddProfileBottomSheet().show(supportFragmentManager, AddProfileBottomSheet.TAG)
74+
}
75+
binding.btnHome.setOnClickListener(::showNavigationMenu)
76+
binding.btnMoreMenu.setOnClickListener(::showNavigationMenu)
7677
binding.fab.setOnClickListener { toggleConnection() }
7778
binding.cardBottomStatus.setOnClickListener { toggleConnection() }
78-
binding.toolbar.setNavigationOnClickListener {
79-
binding.drawerLayout.openDrawer(GravityCompat.START)
80-
}
81-
binding.navView.setCheckedItem(R.id.nav_home)
82-
binding.navView.setNavigationItemSelectedListener { item ->
83-
val target: Class<*>? = when (item.itemId) {
84-
R.id.nav_settings -> SettingsActivity::class.java
85-
R.id.nav_apps -> AppListActivity::class.java
86-
R.id.nav_logcat -> LogcatActivity::class.java
87-
R.id.nav_tools -> ToolsActivity::class.java
88-
R.id.nav_about -> AboutActivity::class.java
89-
else -> null // nav_home: already here
90-
}
91-
target?.let { startActivity(Intent(this, it)) }
92-
binding.navView.setCheckedItem(R.id.nav_home)
93-
binding.drawerLayout.closeDrawer(GravityCompat.START)
94-
true
95-
}
96-
97-
val backCallback = object : OnBackPressedCallback(false) {
98-
override fun handleOnBackPressed() {
99-
binding.drawerLayout.closeDrawer(GravityCompat.START)
100-
}
101-
}
102-
onBackPressedDispatcher.addCallback(this, backCallback)
103-
binding.drawerLayout.addDrawerListener(object : DrawerLayout.SimpleDrawerListener() {
104-
override fun onDrawerOpened(drawerView: View) { backCallback.isEnabled = true }
105-
override fun onDrawerClosed(drawerView: View) { backCallback.isEnabled = false }
106-
})
10779

10880
requestNotificationPermission()
10981
}
@@ -217,6 +189,25 @@ class MainActivity : AppCompatActivity(), AddProfileBottomSheet.Listener {
217189
binding.fab.postDelayed({ refresh() }, 600)
218190
}
219191

192+
private fun showNavigationMenu(anchor: View) {
193+
PopupMenu(this, anchor).apply {
194+
menuInflater.inflate(R.menu.main_drawer_menu, menu)
195+
setOnMenuItemClickListener { item ->
196+
val target = when (item.itemId) {
197+
R.id.nav_settings -> SettingsActivity::class.java
198+
R.id.nav_apps -> AppListActivity::class.java
199+
R.id.nav_logcat -> LogcatActivity::class.java
200+
R.id.nav_tools -> ToolsActivity::class.java
201+
R.id.nav_about -> AboutActivity::class.java
202+
else -> null
203+
}
204+
target?.let { startActivity(Intent(this@MainActivity, it)) }
205+
true
206+
}
207+
show()
208+
}
209+
}
210+
220211
private fun shareProfile(profile: MihomoProfileStore.Profile) {
221212
val url = profile.subscriptionUrl
222213
if (!url.isNullOrBlank()) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package top.uwu.mikubox.ui
2+
3+
import android.content.Intent
4+
import android.os.Bundle
5+
import android.widget.TextView
6+
import androidx.appcompat.app.AppCompatActivity
7+
import androidx.lifecycle.lifecycleScope
8+
import kotlinx.coroutines.delay
9+
import kotlinx.coroutines.launch
10+
import top.uwu.mikubox.R
11+
12+
class SplashActivity : AppCompatActivity() {
13+
override fun onCreate(savedInstanceState: Bundle?) {
14+
super.onCreate(savedInstanceState)
15+
setContentView(R.layout.activity_splash)
16+
val versionName = packageManager.getPackageInfo(packageName, 0).versionName ?: "UwU"
17+
findViewById<TextView>(R.id.splash_version).text =
18+
getString(R.string.splash_version, versionName)
19+
lifecycleScope.launch {
20+
delay(1500)
21+
startActivity(Intent(this@SplashActivity, MainActivity::class.java))
22+
finish()
23+
}
24+
}
25+
26+
@Deprecated("Splash screen does not handle back navigation")
27+
override fun onBackPressed() = Unit
28+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector
3+
xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item
5+
android:color="?colorPrimary"
6+
android:alpha="0.2" />
7+
</selector>
161 KB
Loading
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector
2+
android:tint="?colorOnSurfaceVariant"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:width="24dp"
5+
android:height="24dp"
6+
android:viewportHeight="24"
7+
android:viewportWidth="24">
8+
9+
<path
10+
android:fillColor="#ff000000"
11+
android:pathData="M4.93,3.93C3.12,5.74 2,8.24 2,11C2,13.76 3.12,16.26 4.93,18.07L6.34,16.66C4.89,15.22 4,13.22 4,11C4,8.79 4.89,6.78 6.34,5.34L4.93,3.93M19.07,3.93L17.66,5.34C19.11,6.78 20,8.79 20,11C20,13.22 19.11,15.22 17.66,16.66L19.07,18.07C20.88,16.26 22,13.76 22,11C22,8.24 20.88,5.74 19.07,3.93M7.76,6.76C6.67,7.85 6,9.35 6,11C6,12.65 6.67,14.15 7.76,15.24L9.17,13.83C8.45,13.11 8,12.11 8,11C8,9.89 8.45,8.89 9.17,8.17L7.76,6.76M16.24,6.76L14.83,8.17C15.55,8.89 16,9.89 16,11C16,12.11 15.55,13.11 14.83,13.83L16.24,15.24C17.33,14.15 18,12.65 18,11C18,9.35 17.33,7.85 16.24,6.76M12,9A2,2 0,0 0,10 11A2,2 0,0 0,12 13A2,2 0,0 0,14 11A2,2 0,0 0,12 9M11,15V19H10A1,1 0,0 0,9 20H2V22H9A1,1 0,0 0,10 23H14A1,1 0,0 0,15 22H22V20H15A1,1 0,0 0,14 19H13V15H11Z"/>
12+
13+
</vector>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<vector
2+
xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="24dp"
4+
android:height="24dp"
5+
android:viewportHeight="24"
6+
android:viewportWidth="24">
7+
8+
<path
9+
android:fillColor="#ff000000"
10+
android:pathData="M10,4A4,4 0,0 0,6 8A4,4 0,0 0,10 12A4,4 0,0 0,14 8A4,4 0,0 0,10 4M10,6A2,2 0,0 1,12 8A2,2 0,0 1,10 10A2,2 0,0 1,8 8A2,2 0,0 1,10 6M17,12C16.84,12 16.76,12.08 16.76,12.24L16.5,13.5C16.28,13.68 15.96,13.84 15.72,14L14.44,13.5C14.36,13.5 14.2,13.5 14.12,13.6L13.16,15.36C13.08,15.44 13.08,15.6 13.24,15.68L14.28,16.5V17.5L13.24,18.32C13.16,18.4 13.08,18.56 13.16,18.64L14.12,20.4C14.2,20.5 14.36,20.5 14.44,20.5L15.72,20C15.96,20.16 16.28,20.32 16.5,20.5L16.76,21.76C16.76,21.92 16.84,22 17,22H19C19.08,22 19.24,21.92 19.24,21.76L19.4,20.5C19.72,20.32 20.04,20.16 20.28,20L21.5,20.5C21.64,20.5 21.8,20.5 21.8,20.4L22.84,18.64C22.92,18.56 22.84,18.4 22.76,18.32L21.72,17.5V16.5L22.76,15.68C22.84,15.6 22.92,15.44 22.84,15.36L21.8,13.6C21.8,13.5 21.64,13.5 21.5,13.5L20.28,14C20.04,13.84 19.72,13.68 19.4,13.5L19.24,12.24C19.24,12.08 19.08,12 19,12H17M10,13C7.33,13 2,14.33 2,17V20H11.67C11.39,19.41 11.19,18.77 11.09,18.1H3.9V17C3.9,16.36 7.03,14.9 10,14.9C10.43,14.9 10.87,14.94 11.3,15C11.5,14.36 11.77,13.76 12.12,13.21C11.34,13.08 10.6,13 10,13M18.04,15.5C18.84,15.5 19.5,16.16 19.5,17.04C19.5,17.84 18.84,18.5 18.04,18.5C17.16,18.5 16.5,17.84 16.5,17.04C16.5,16.16 17.16,15.5 18.04,15.5Z"/>
11+
12+
</vector>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector
2+
android:tint="?colorOnSurfaceVariant"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:width="24dp"
5+
android:height="24dp"
6+
android:viewportHeight="24"
7+
android:viewportWidth="24">
8+
9+
<path
10+
android:fillColor="#ff000000"
11+
android:pathData="M2,17V20H10V18.11H3.9V17C3.9,16.36 7.03,14.9 10,14.9C10.96,14.91 11.91,15.04 12.83,15.28L14.35,13.76C12.95,13.29 11.5,13.03 10,13C7.33,13 2,14.33 2,17M10,4C7.79,4 6,5.79 6,8S7.79,12 10,12 14,10.21 14,8 12.21,4 10,4M10,10C8.9,10 8,9.11 8,8S8.9,6 10,6 12,6.9 12,8 11.11,10 10,10M21.7,13.35L20.7,14.35L18.65,12.35L19.65,11.35C19.86,11.14 20.21,11.14 20.42,11.35L21.7,12.63C21.91,12.84 21.91,13.19 21.7,13.4M12,18.94L18.06,12.88L20.11,14.88L14.11,20.95H12V18.94"/>
12+
13+
</vector>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector
2+
android:tint="?colorOnSurfaceVariant"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:width="24dp"
5+
android:height="24dp"
6+
android:viewportHeight="24"
7+
android:viewportWidth="24">
8+
9+
<path
10+
android:fillColor="#ff000000"
11+
android:pathData="M19,3H14.82C14.25,1.44 12.53,0.64 11,1.2C10.14,1.5 9.5,2.16 9.18,3H5A2,2 0,0 0,3 5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V5A2,2 0,0 0,19 3M12,3A1,1 0,0 1,13 4A1,1 0,0 1,12 5A1,1 0,0 1,11 4A1,1 0,0 1,12 3M7,7H17V5H19V19H5V5H7V7M17,11H7V9H17V11M15,15H7V13H15V15Z"/>
12+
13+
</vector>

0 commit comments

Comments
 (0)