diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e405e9a..0000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea -.DS_Store -/build -/captures -.externalNativeBuild -.cxx -local.properties -/tmp diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7203af6..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 shuiyu - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 4e0bb4f..c90db1e 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ 你好呀~这个仓库已经搬到更好用的地方啦。 -为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到 -**Gitee** 与 **GitHub** 主仓维护,这里就只保留这张"迁移小纸条"咯。 +为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到主仓维护,这里就只保留这张"迁移小纸条"咯。 📦 新家在这里: - Gitee:https://gitee.com/shuiyu1123/mimo-studio-android diff --git a/app/build.gradle.kts b/app/build.gradle.kts deleted file mode 100644 index 0e82ffd..0000000 --- a/app/build.gradle.kts +++ /dev/null @@ -1,66 +0,0 @@ -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.plugin.serialization") version "2.0.21" -} - -android { - namespace = "com.mimo.studio" - compileSdk = 35 - - defaultConfig { - applicationId = "com.mimo.studio" - minSdk = 26 - targetSdk = 35 - versionCode = 1 - versionName = "1.0.0" - } - - buildTypes { - release { - isMinifyEnabled = true - isShrinkResources = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - kotlinOptions { - jvmTarget = "17" - } - - buildFeatures { - viewBinding = true - } -} - -dependencies { - implementation("androidx.core:core-ktx:1.15.0") - implementation("androidx.appcompat:appcompat:1.7.0") - implementation("com.google.android.material:material:1.12.0") - implementation("androidx.constraintlayout:constraintlayout:2.2.1") - implementation("androidx.recyclerview:recyclerview:1.4.0") - implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") - implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") - - // Retrofit + OkHttp - implementation("com.squareup.retrofit2:retrofit:2.11.0") - implementation("com.squareup.retrofit2:converter-gson:2.11.0") - implementation("com.squareup.okhttp3:okhttp:4.12.0") - implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") - implementation("com.squareup.okhttp3:okhttp-sse:4.12.0") - - // Gson - implementation("com.google.code.gson:gson:2.11.0") - - // Coroutines - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") -} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro deleted file mode 100644 index 65d549e..0000000 --- a/app/proguard-rules.pro +++ /dev/null @@ -1,5 +0,0 @@ --dontwarn okhttp3.** --dontwarn okio.** --dontwarn javax.annotation.** --keep class com.mimo.studio.model.** { *; } --keep class com.mimo.studio.api.** { *; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml deleted file mode 100644 index 7865050..0000000 --- a/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/mimo/studio/MainActivity.kt b/app/src/main/java/com/mimo/studio/MainActivity.kt deleted file mode 100644 index e593924..0000000 --- a/app/src/main/java/com/mimo/studio/MainActivity.kt +++ /dev/null @@ -1,266 +0,0 @@ -package com.mimo.studio - -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.widget.* -import androidx.appcompat.app.ActionBarDrawerToggle -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.GravityCompat -import androidx.drawerlayout.widget.DrawerLayout -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.appbar.MaterialToolbar -import com.google.android.material.button.MaterialButton -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.navigation.NavigationView -import com.google.android.material.switchmaterial.SwitchMaterial -import com.google.android.material.textfield.TextInputEditText -import com.mimo.studio.api.RetrofitClient -import com.mimo.studio.model.ChatMessage -import com.mimo.studio.model.ModelInfo -import com.mimo.studio.ui.ChatAdapter -import com.mimo.studio.ui.ModelsActivity -import com.mimo.studio.viewmodel.MainViewModel -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch - -class MainActivity : AppCompatActivity() { - - private lateinit var viewModel: MainViewModel - private lateinit var chatAdapter: ChatAdapter - private lateinit var drawerLayout: DrawerLayout - private lateinit var recyclerChat: RecyclerView - private lateinit var etMessage: EditText - private lateinit var btnSend: ImageButton - private lateinit var progressBar: ProgressBar - private lateinit var tvStreaming: TextView - private lateinit var spinnerModel: Spinner - - private var modelNames = listOf("xiaomi/mimo-v2-flash", "xiaomi/mimo-v2-pro", "xiaomi/mimo-v2-omni", "xiaomi/mimo-v2.5-pro", "xiaomi/mimo-v2.5") - private var modelDisplayNames = listOf("MiMo-V2-Flash", "MiMo-V2-Pro", "MiMo-V2-Omni", "MiMo-V2.5-Pro", "MiMo-V2.5") - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - viewModel = MainViewModel(application) - - initViews() - setupDrawer() - setupChat() - setupModelSpinner() - observeState() - - // Check if API key is configured - if (viewModel.getApiKey().isBlank()) { - showSettingsDialog() - } else { - viewModel.fetchModels() - } - } - - private fun initViews() { - drawerLayout = findViewById(R.id.drawerLayout) - recyclerChat = findViewById(R.id.recyclerChat) - etMessage = findViewById(R.id.etMessage) - btnSend = findViewById(R.id.btnSend) - progressBar = findViewById(R.id.progressBar) - tvStreaming = findViewById(R.id.tvStreaming) - spinnerModel = findViewById(R.id.spinnerModel) - } - - private fun setupDrawer() { - val toolbar = findViewById(R.id.toolbar) - val toggle = ActionBarDrawerToggle( - this, drawerLayout, toolbar, - R.string.app_name, R.string.app_name - ) - drawerLayout.addDrawerListener(toggle) - toggle.syncState() - - val navView = findViewById(R.id.navView) - navView.setNavigationItemSelectedListener { item -> - drawerLayout.closeDrawer(GravityCompat.START) - when (item.itemId) { - R.id.nav_new_chat -> viewModel.newConversation() - R.id.nav_models -> { - startActivity(Intent(this, ModelsActivity::class.java)) - } - R.id.nav_settings -> showSettingsDialog() - } - true - } - } - - private fun setupChat() { - chatAdapter = ChatAdapter() - recyclerChat.layoutManager = LinearLayoutManager(this).apply { - stackFromEnd = true - } - recyclerChat.adapter = chatAdapter - - btnSend.setOnClickListener { - val text = etMessage.text.toString().trim() - if (text.isNotEmpty()) { - viewModel.sendMessage(text) - etMessage.text.clear() - } - } - } - - private fun setupModelSpinner() { - val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, modelDisplayNames) - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinnerModel.adapter = adapter - - spinnerModel.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int, id: Long) { - viewModel.setSelectedModel(modelNames[pos]) - } - override fun onNothingSelected(parent: AdapterView<*>?) {} - } - } - - private fun observeState() { - lifecycleScope.launch { - viewModel.currentConversation.collectLatest { conv -> - if (conv != null) { - chatAdapter.submitList(conv.messages.toList()) - if (conv.messages.isNotEmpty()) { - recyclerChat.post { - recyclerChat.smoothScrollToPosition(conv.messages.size - 1) - } - } - } - } - } - - lifecycleScope.launch { - viewModel.isLoading.collectLatest { loading -> - progressBar.visibility = if (loading) View.VISIBLE else View.GONE - btnSend.isEnabled = !loading - } - } - - lifecycleScope.launch { - viewModel.isStreaming.collectLatest { streaming -> - tvStreaming.visibility = if (streaming) View.VISIBLE else View.GONE - } - } - - lifecycleScope.launch { - viewModel.streamingContent.collectLatest { content -> - if (content.isNotEmpty()) { - tvStreaming.text = content - chatAdapter.updateStreamingContent(content) - recyclerChat.post { - recyclerChat.smoothScrollToPosition(chatAdapter.itemCount - 1) - } - } - } - } - - lifecycleScope.launch { - viewModel.error.collectLatest { error -> - error?.let { - Toast.makeText(this@MainActivity, it, Toast.LENGTH_LONG).show() - viewModel.clearError() - } - } - } - - lifecycleScope.launch { - viewModel.selectedModel.collectLatest { model -> - val idx = modelNames.indexOf(model) - if (idx >= 0 && spinnerModel.selectedItemPosition != idx) { - spinnerModel.setSelection(idx) - } - } - } - - lifecycleScope.launch { - viewModel.models.collectLatest { models -> - if (models.isNotEmpty()) { - updateModelList(models) - } - } - } - } - - private fun updateModelList(models: List) { - modelNames = models.map { it.id } - modelDisplayNames = models.map { it.name.removePrefix("Xiaomi MiMo:").removePrefix("Xiaomi MiMo:") } - val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, modelDisplayNames) - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinnerModel.adapter = adapter - - val idx = modelNames.indexOf(viewModel.selectedModel.value) - if (idx >= 0) spinnerModel.setSelection(idx) - } - - private fun showSettingsDialog() { - val dialogView = layoutInflater.inflate(R.layout.dialog_settings, null) - val etApiKey = dialogView.findViewById(R.id.etApiKey) - val etBaseUrl = dialogView.findViewById(R.id.etBaseUrl) - val etSystemPrompt = dialogView.findViewById(R.id.etSystemPrompt) - val seekTemp = dialogView.findViewById(R.id.seekTemperature) - val tvTemp = dialogView.findViewById(R.id.tvTemperature) - val etMaxTokens = dialogView.findViewById(R.id.etMaxTokens) - val switchStreaming = dialogView.findViewById(R.id.switchStreaming) - - etApiKey.setText(viewModel.getApiKey()) - etBaseUrl.setText(viewModel.getBaseUrl()) - etSystemPrompt.setText(viewModel.currentConversation.value?.systemPrompt ?: "") - seekTemp.progress = (viewModel.temperature.value * 100).toInt() - tvTemp.text = String.format("%.2f", viewModel.temperature.value) - etMaxTokens.setText(viewModel.maxTokens.value.toString()) - switchStreaming.isChecked = viewModel.useStreaming.value - - seekTemp.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { - override fun onProgressChanged(sb: SeekBar?, progress: Int, fromUser: Boolean) { - tvTemp.text = String.format("%.2f", progress / 100f) - } - override fun onStartTrackingTouch(sb: SeekBar?) {} - override fun onStopTrackingTouch(sb: SeekBar?) {} - }) - - MaterialAlertDialogBuilder(this) - .setTitle("设置") - .setView(dialogView) - .setPositiveButton("保存") { _, _ -> - val apiKey = etApiKey.text.toString().trim() - val baseUrl = etBaseUrl.text.toString().trim().ifEmpty { "https://api.xiaomimimo.com/" } - val systemPrompt = etSystemPrompt.text.toString().trim() - val temp = seekTemp.progress / 100f - val maxTokens = etMaxTokens.text.toString().trim().toIntOrNull() ?: 4096 - - viewModel.saveSettings(apiKey, baseUrl) - viewModel.setTemperature(temp) - viewModel.setMaxTokens(maxTokens) - viewModel.setStreaming(switchStreaming.isChecked) - - // Update system prompt on current conversation - viewModel.currentConversation.value?.let { conv -> - val updated = conv.copy(systemPrompt = systemPrompt) - // Update through viewmodel would be cleaner, but this works - } - - if (apiKey.isNotBlank()) { - viewModel.fetchModels() - } - } - .setNegativeButton("取消", null) - .show() - } - - @Deprecated("Use OnBackPressedCallback") - override fun onBackPressed() { - if (drawerLayout.isDrawerOpen(GravityCompat.START)) { - drawerLayout.closeDrawer(GravityCompat.START) - } else { - super.onBackPressed() - } - } -} diff --git a/app/src/main/java/com/mimo/studio/api/MimoApiService.kt b/app/src/main/java/com/mimo/studio/api/MimoApiService.kt deleted file mode 100644 index 4d9fed8..0000000 --- a/app/src/main/java/com/mimo/studio/api/MimoApiService.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.mimo.studio.api - -import com.mimo.studio.model.* -import okhttp3.ResponseBody -import retrofit2.http.* - -interface MimoApiService { - - /** List all available models */ - @GET("v1/models") - suspend fun listModels(): ModelsResponse - - /** Get a specific model */ - @GET("v1/models/{model_id}") - suspend fun getModel(@Path("model_id") modelId: String): ModelInfo - - /** Create a chat completion */ - @POST("v1/chat/completions") - suspend fun chatCompletions(@Body request: ChatRequest): ChatResponse - - /** Create a streaming chat completion */ - @POST("v1/chat/completions") - @Streaming - suspend fun chatCompletionsStream(@Body request: ChatRequest): ResponseBody - - /** Create a text completion */ - @POST("v1/completions") - suspend fun completions(@Body request: CompletionRequest): CompletionResponse - - /** Create a streaming text completion */ - @POST("v1/completions") - @Streaming - suspend fun completionsStream(@Body request: CompletionRequest): ResponseBody -} diff --git a/app/src/main/java/com/mimo/studio/api/RetrofitClient.kt b/app/src/main/java/com/mimo/studio/api/RetrofitClient.kt deleted file mode 100644 index 56a8c84..0000000 --- a/app/src/main/java/com/mimo/studio/api/RetrofitClient.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.mimo.studio.api - -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import java.util.concurrent.TimeUnit - -object RetrofitClient { - - private const val BASE_URL = "https://api.xiaomimimo.com/" - - private var apiKey: String = "" - private var baseUrl: String = BASE_URL - - fun configure(apiKey: String, baseUrl: String = BASE_URL) { - this.apiKey = apiKey - this.baseUrl = baseUrl - } - - fun isConfigured(): Boolean = apiKey.isNotBlank() - - private val authInterceptor = Interceptor { chain -> - val original = chain.request() - val request = original.newBuilder() - .header("Authorization", "Bearer $apiKey") - .header("Content-Type", "application/json") - .build() - chain.proceed(request) - } - - private val loggingInterceptor = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - - private val okHttpClient = OkHttpClient.Builder() - .addInterceptor(authInterceptor) - .addInterceptor(loggingInterceptor) - .connectTimeout(60, TimeUnit.SECONDS) - .readTimeout(120, TimeUnit.SECONDS) - .writeTimeout(60, TimeUnit.SECONDS) - .build() - - private var retrofit: Retrofit? = null - private var currentBaseUrl: String = "" - - val api: MimoApiService - get() { - if (retrofit == null || currentBaseUrl != baseUrl) { - retrofit = Retrofit.Builder() - .baseUrl(baseUrl) - .client(okHttpClient) - .addConverterFactory(GsonConverterFactory.create()) - .build() - currentBaseUrl = baseUrl - } - return retrofit!!.create(MimoApiService::class.java) - } -} diff --git a/app/src/main/java/com/mimo/studio/model/ApiModels.kt b/app/src/main/java/com/mimo/studio/model/ApiModels.kt deleted file mode 100644 index 086ea7d..0000000 --- a/app/src/main/java/com/mimo/studio/model/ApiModels.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.mimo.studio.model - -import com.google.gson.annotations.SerializedName - -// ===== Models API ===== -data class ModelsResponse( - val data: List -) - -data class ModelInfo( - val id: String, - val name: String, - val description: String, - @SerializedName("context_length") val contextLength: Int, - @SerializedName("max_output_length") val maxOutputLength: Int, - val architecture: Architecture?, - val pricing: Any?, // Can be object or array - @SerializedName("supported_parameters") val supportedParameters: List? -) - -data class Architecture( - val modality: String, - @SerializedName("input_modalities") val inputModalities: List, - @SerializedName("output_modalities") val outputModalities: List -) - -// ===== Chat Completions API ===== -data class ChatRequest( - val model: String, - val messages: List, - val stream: Boolean = false, - @SerializedName("max_tokens") val maxTokens: Int? = null, - val temperature: Float? = null, - @SerializedName("top_p") val topP: Float? = null, - val stop: List? = null, - @SerializedName("frequency_penalty") val frequencyPenalty: Float? = null, - @SerializedName("presence_penalty") val presencePenalty: Float? = null, - @SerializedName("response_format") val responseFormat: ResponseFormat? = null, - val reasoning: Boolean? = null, - @SerializedName("include_reasoning") val includeReasoning: Boolean? = null -) - -data class ResponseFormat( - val type: String // "text" or "json_object" -) - -data class ChatMessage( - val role: String, // "system", "user", "assistant" - val content: String -) - -data class ChatResponse( - val id: String?, - val choices: List?, - val usage: Usage?, - val model: String? -) - -data class Choice( - val index: Int, - val message: ChatMessage?, - @SerializedName("finish_reason") val finishReason: String?, - val delta: ChatMessage? // for streaming -) - -data class Usage( - @SerializedName("prompt_tokens") val promptTokens: Int, - @SerializedName("completion_tokens") val completionTokens: Int, - @SerializedName("total_tokens") val totalTokens: Int -) - -// ===== Streaming ===== -data class StreamChunk( - val id: String?, - val choices: List?, - val model: String? -) - -data class StreamChoice( - val index: Int, - val delta: DeltaMessage?, - @SerializedName("finish_reason") val finishReason: String? -) - -data class DeltaMessage( - val role: String?, - val content: String? -) - -// ===== Completions API ===== -data class CompletionRequest( - val model: String, - val prompt: String, - val stream: Boolean = false, - @SerializedName("max_tokens") val maxTokens: Int? = null, - val temperature: Float? = null, - @SerializedName("top_p") val topP: Float? = null, - val stop: List? = null -) - -data class CompletionResponse( - val id: String?, - val choices: List?, - val usage: Usage?, - val model: String? -) - -data class CompletionChoice( - val index: Int, - val text: String?, - @SerializedName("finish_reason") val finishReason: String? -) - -// ===== Local chat state ===== -data class Conversation( - val id: String = java.util.UUID.randomUUID().toString(), - val title: String = "新对话", - val messages: MutableList = mutableListOf(), - val model: String = "xiaomi/mimo-v2-flash", - val systemPrompt: String = "", - val createdAt: Long = System.currentTimeMillis() -) diff --git a/app/src/main/java/com/mimo/studio/ui/ChatAdapter.kt b/app/src/main/java/com/mimo/studio/ui/ChatAdapter.kt deleted file mode 100644 index 2283b26..0000000 --- a/app/src/main/java/com/mimo/studio/ui/ChatAdapter.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.mimo.studio.ui - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.mimo.studio.R -import com.mimo.studio.model.ChatMessage -import java.text.SimpleDateFormat -import java.util.* - -class ChatAdapter : ListAdapter(ChatDiffCallback()) { - - companion object { - private const val TYPE_USER = 0 - private const val TYPE_ASSISTANT = 1 - private const val TYPE_SYSTEM = 2 - } - - private var timestamp = System.currentTimeMillis() - - override fun getItemViewType(position: Int): Int { - return when (getItem(position).role) { - "user" -> TYPE_USER - "assistant" -> TYPE_ASSISTANT - else -> TYPE_SYSTEM - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val inflater = LayoutInflater.from(parent.context) - return when (viewType) { - TYPE_USER -> UserViewHolder(inflater.inflate(R.layout.item_chat_user, parent, false)) - TYPE_ASSISTANT -> AssistantViewHolder(inflater.inflate(R.layout.item_chat_assistant, parent, false)) - else -> SystemViewHolder(inflater.inflate(R.layout.item_chat_assistant, parent, false)) - } - } - - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - val message = getItem(position) - val timeStr = SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date(timestamp + position * 1000)) - when (holder) { - is UserViewHolder -> holder.bind(message, timeStr) - is AssistantViewHolder -> holder.bind(message, timeStr) - is SystemViewHolder -> holder.bind(message, timeStr) - } - } - - fun updateStreamingContent(content: String) { - // Update the last assistant message with streaming content - // or add a temporary one - val currentList = currentList.toMutableList() - if (currentList.isNotEmpty() && currentList.last().role == "assistant" && currentList.last().content == "") { - currentList[currentList.size - 1] = ChatMessage(role = "assistant", content = content) - } else { - currentList.add(ChatMessage(role = "assistant", content = content)) - } - submitList(currentList) - } - - class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvMessage: TextView = itemView.findViewById(R.id.tvMessage) - private val tvTimestamp: TextView = itemView.findViewById(R.id.tvTimestamp) - - fun bind(message: ChatMessage, time: String) { - tvMessage.text = message.content - tvTimestamp.text = time - } - } - - class AssistantViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvMessage: TextView = itemView.findViewById(R.id.tvMessage) - private val tvTimestamp: TextView = itemView.findViewById(R.id.tvTimestamp) - - fun bind(message: ChatMessage, time: String) { - tvMessage.text = message.content - tvTimestamp.text = time - } - } - - class SystemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvMessage: TextView = itemView.findViewById(R.id.tvMessage) - private val tvTimestamp: TextView = itemView.findViewById(R.id.tvTimestamp) - - fun bind(message: ChatMessage, time: String) { - tvMessage.text = "⚙️ ${message.content}" - tvTimestamp.text = time - } - } - - class ChatDiffCallback : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean { - return oldItem === newItem - } - - override fun areContentsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean { - return oldItem.role == newItem.role && oldItem.content == newItem.content - } - } -} diff --git a/app/src/main/java/com/mimo/studio/ui/ModelsActivity.kt b/app/src/main/java/com/mimo/studio/ui/ModelsActivity.kt deleted file mode 100644 index 969fe27..0000000 --- a/app/src/main/java/com/mimo/studio/ui/ModelsActivity.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.mimo.studio.ui - -import android.os.Bundle -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.appbar.MaterialToolbar -import com.mimo.studio.R -import com.mimo.studio.api.RetrofitClient -import com.mimo.studio.model.ModelInfo -import kotlinx.coroutines.launch - -class ModelsActivity : AppCompatActivity() { - - private lateinit var adapter: ModelsAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_models) - - val toolbar = findViewById(R.id.toolbar) - toolbar.setNavigationOnClickListener { finish() } - - adapter = ModelsAdapter { model -> - val intent = intent.putExtra("selected_model", model.id) - setResult(RESULT_OK, intent) - Toast.makeText(this, "已选择: ${model.name}", Toast.LENGTH_SHORT).show() - finish() - } - - val recycler = findViewById(R.id.recyclerModels) - recycler.layoutManager = LinearLayoutManager(this) - recycler.adapter = adapter - - val swipeRefresh = findViewById(R.id.swipeRefresh) - swipeRefresh.setOnRefreshListener { fetchModels() } - - fetchModels() - } - - private fun fetchModels() { - val swipeRefresh = findViewById(R.id.swipeRefresh) - lifecycleScope.launch { - try { - val response = RetrofitClient.api.listModels() - adapter.submitList(response.data) - } catch (e: Exception) { - Toast.makeText(this@ModelsActivity, "加载失败: ${e.message}", Toast.LENGTH_LONG).show() - } finally { - swipeRefresh.isRefreshing = false - } - } - } -} diff --git a/app/src/main/java/com/mimo/studio/ui/ModelsAdapter.kt b/app/src/main/java/com/mimo/studio/ui/ModelsAdapter.kt deleted file mode 100644 index 258debf..0000000 --- a/app/src/main/java/com/mimo/studio/ui/ModelsAdapter.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.mimo.studio.ui - -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.button.MaterialButton -import com.mimo.studio.R -import com.mimo.studio.model.ModelInfo - -class ModelsAdapter( - private val onSelect: (ModelInfo) -> Unit -) : RecyclerView.Adapter() { - - private var models: List = emptyList() - - fun submitList(list: List) { - models = list - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModelViewHolder { - val view = LayoutInflater.from(parent.context).inflate(R.layout.item_model, parent, false) - return ModelViewHolder(view) - } - - override fun onBindViewHolder(holder: ModelViewHolder, position: Int) { - holder.bind(models[position]) - } - - override fun getItemCount() = models.size - - inner class ModelViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val tvName: TextView = itemView.findViewById(R.id.tvModelName) - private val tvId: TextView = itemView.findViewById(R.id.tvModelId) - private val tvModality: TextView = itemView.findViewById(R.id.tvModality) - private val tvContext: TextView = itemView.findViewById(R.id.tvContext) - private val tvDesc: TextView = itemView.findViewById(R.id.tvDescription) - private val btnSelect: MaterialButton = itemView.findViewById(R.id.btnSelect) - - fun bind(model: ModelInfo) { - tvName.text = model.name - tvId.text = model.id - tvModality.text = "模态: ${model.architecture?.modality ?: "text->text"}" - tvContext.text = "上下文: ${formatTokens(model.contextLength)} tokens | 最大输出: ${formatTokens(model.maxOutputLength)} tokens" - tvDesc.text = model.description - btnSelect.setOnClickListener { onSelect(model) } - } - - private fun formatTokens(tokens: Int): String { - return when { - tokens >= 1_000_000 -> "${tokens / 1_000_000}M" - tokens >= 1_000 -> "${tokens / 1_000}K" - else -> tokens.toString() - } - } - } -} diff --git a/app/src/main/java/com/mimo/studio/viewmodel/MainViewModel.kt b/app/src/main/java/com/mimo/studio/viewmodel/MainViewModel.kt deleted file mode 100644 index e42333f..0000000 --- a/app/src/main/java/com/mimo/studio/viewmodel/MainViewModel.kt +++ /dev/null @@ -1,303 +0,0 @@ -package com.mimo.studio.viewmodel - -import android.app.Application -import android.content.Context -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.viewModelScope -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import com.mimo.studio.api.RetrofitClient -import com.mimo.studio.model.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.BufferedReader -import java.io.InputStreamReader - -class MainViewModel(application: Application) : AndroidViewModel(application) { - - private val prefs = application.getSharedPreferences("mimo_prefs", Context.MODE_PRIVATE) - private val gson = Gson() - - // State - private val _models = MutableStateFlow>(emptyList()) - val models: StateFlow> = _models - - private val _conversations = MutableStateFlow>(emptyList()) - val conversations: StateFlow> = _conversations - - private val _currentConversation = MutableStateFlow(null) - val currentConversation: StateFlow = _currentConversation - - private val _isLoading = MutableStateFlow(false) - val isLoading: StateFlow = _isLoading - - private val _isStreaming = MutableStateFlow(false) - val isStreaming: StateFlow = _isStreaming - - private val _streamingContent = MutableStateFlow("") - val streamingContent: StateFlow = _streamingContent - - private val _error = MutableStateFlow(null) - val error: StateFlow = _error - - private val _selectedModel = MutableStateFlow("xiaomi/mimo-v2-flash") - val selectedModel: StateFlow = _selectedModel - - private val _useStreaming = MutableStateFlow(true) - val useStreaming: StateFlow = _useStreaming - - private val _temperature = MutableStateFlow(1.0f) - val temperature: StateFlow = _temperature - - private val _maxTokens = MutableStateFlow(4096) - val maxTokens: StateFlow = _maxTokens - - init { - loadSettings() - loadConversations() - if (conversations.value.isEmpty()) { - newConversation() - } - } - - // ===== Settings ===== - fun getApiKey(): String = prefs.getString("api_key", "") ?: "" - fun getBaseUrl(): String = prefs.getString("base_url", "https://api.xiaomimimo.com/") ?: "https://api.xiaomimimo.com/" - - fun saveSettings(apiKey: String, baseUrl: String) { - prefs.edit() - .putString("api_key", apiKey) - .putString("base_url", baseUrl) - .apply() - RetrofitClient.configure(apiKey, baseUrl) - } - - private fun loadSettings() { - val key = getApiKey() - val url = getBaseUrl() - if (key.isNotBlank()) { - RetrofitClient.configure(key, url) - } - _selectedModel.value = prefs.getString("selected_model", "xiaomi/mimo-v2-flash") ?: "xiaomi/mimo-v2-flash" - _useStreaming.value = prefs.getBoolean("use_streaming", true) - _temperature.value = prefs.getFloat("temperature", 1.0f) - _maxTokens.value = prefs.getInt("max_tokens", 4096) - } - - fun setSelectedModel(model: String) { - _selectedModel.value = model - prefs.edit().putString("selected_model", model).apply() - } - - fun setStreaming(enabled: Boolean) { - _useStreaming.value = enabled - prefs.edit().putBoolean("use_streaming", enabled).apply() - } - - fun setTemperature(temp: Float) { - _temperature.value = temp - prefs.edit().putFloat("temperature", temp).apply() - } - - fun setMaxTokens(tokens: Int) { - _maxTokens.value = tokens - prefs.edit().putInt("max_tokens", tokens).apply() - } - - // ===== Models ===== - fun fetchModels() { - if (!RetrofitClient.isConfigured()) { - _error.value = "请先配置 API Key" - return - } - viewModelScope.launch { - try { - val response = withContext(Dispatchers.IO) { RetrofitClient.api.listModels() } - _models.value = response.data - _error.value = null - } catch (e: Exception) { - _error.value = "获取模型列表失败: ${e.message}" - } - } - } - - // ===== Conversations ===== - fun newConversation() { - val conv = Conversation(model = _selectedModel.value) - val list = _conversations.value.toMutableList() - list.add(0, conv) - _conversations.value = list - _currentConversation.value = conv - saveConversations() - } - - fun selectConversation(id: String) { - _currentConversation.value = _conversations.value.find { it.id == id } - } - - fun deleteConversation(id: String) { - val list = _conversations.value.filter { it.id != id }.toMutableList() - _conversations.value = list - if (_currentConversation.value?.id == id) { - _currentConversation.value = list.firstOrNull() - } - saveConversations() - } - - private fun saveConversations() { - val json = gson.toJson(_conversations.value) - prefs.edit().putString("conversations", json).apply() - } - - private fun loadConversations() { - val json = prefs.getString("conversations", null) - if (json != null) { - val type = object : TypeToken>() {}.type - try { - val list: List = gson.fromJson(json, type) - _conversations.value = list - _currentConversation.value = list.firstOrNull() - } catch (_: Exception) {} - } - } - - // ===== Chat ===== - fun sendMessage(content: String) { - val conv = _currentConversation.value ?: return - if (!RetrofitClient.isConfigured()) { - _error.value = "请先在设置中配置 API Key" - return - } - - // Add user message - conv.messages.add(ChatMessage(role = "user", content = content)) - if (conv.messages.size == 1 && conv.title == "新对话") { - // Auto-title from first message - val newConv = conv.copy(title = content.take(30) + if (content.length > 30) "..." else "") - updateConversation(newConv) - } else { - _currentConversation.value = conv.copy() - } - - if (_useStreaming.value) { - sendStreamingMessage(conv) - } else { - sendNormalMessage(conv) - } - } - - private fun sendNormalMessage(conv: Conversation) { - _isLoading.value = true - _error.value = null - - viewModelScope.launch { - try { - val messages = buildMessages(conv) - val request = ChatRequest( - model = conv.model, - messages = messages, - temperature = _temperature.value, - maxTokens = _maxTokens.value - ) - val response = withContext(Dispatchers.IO) { - RetrofitClient.api.chatCompletions(request) - } - val assistantMsg = response.choices?.firstOrNull()?.message - if (assistantMsg != null) { - conv.messages.add(assistantMsg) - updateConversation(conv.copy()) - } - _error.value = null - } catch (e: Exception) { - _error.value = "请求失败: ${e.message}" - } finally { - _isLoading.value = false - } - } - } - - private fun sendStreamingMessage(conv: Conversation) { - _isStreaming.value = true - _streamingContent.value = "" - _error.value = null - - viewModelScope.launch { - try { - val messages = buildMessages(conv) - val request = ChatRequest( - model = conv.model, - messages = messages, - stream = true, - temperature = _temperature.value, - maxTokens = _maxTokens.value - ) - val responseBody = withContext(Dispatchers.IO) { - RetrofitClient.api.chatCompletionsStream(request) - } - - val reader = BufferedReader(InputStreamReader(responseBody.byteStream())) - val fullContent = StringBuilder() - - try { - var line: String? - while (reader.readLine().also { line = it } != null) { - val l = line ?: continue - if (!l.startsWith("data: ")) continue - val data = l.removePrefix("data: ").trim() - if (data == "[DONE]") break - - try { - val chunk = gson.fromJson(data, StreamChunk::class.java) - val delta = chunk.choices?.firstOrNull()?.delta - if (delta?.content != null) { - fullContent.append(delta.content) - _streamingContent.value = fullContent.toString() - } - } catch (_: Exception) {} - } - } finally { - reader.close() - responseBody.close() - } - - if (fullContent.isNotEmpty()) { - conv.messages.add(ChatMessage(role = "assistant", content = fullContent.toString())) - updateConversation(conv.copy()) - } - _streamingContent.value = "" - } catch (e: Exception) { - _error.value = "流式请求失败: ${e.message}" - _streamingContent.value = "" - } finally { - _isStreaming.value = false - } - } - } - - private fun buildMessages(conv: Conversation): List { - val messages = mutableListOf() - if (conv.systemPrompt.isNotBlank()) { - messages.add(ChatMessage(role = "system", content = conv.systemPrompt)) - } - messages.addAll(conv.messages) - return messages - } - - private fun updateConversation(conv: Conversation) { - _currentConversation.value = conv - val list = _conversations.value.toMutableList() - val idx = list.indexOfFirst { it.id == conv.id } - if (idx >= 0) { - list[idx] = conv - } - _conversations.value = list - saveConversations() - } - - fun clearError() { - _error.value = null - } -} diff --git a/app/src/main/res/drawable/bg_assistant_bubble.xml b/app/src/main/res/drawable/bg_assistant_bubble.xml deleted file mode 100644 index 49633a5..0000000 --- a/app/src/main/res/drawable/bg_assistant_bubble.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/bg_card.xml b/app/src/main/res/drawable/bg_card.xml deleted file mode 100644 index 58b70d8..0000000 --- a/app/src/main/res/drawable/bg_card.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/bg_input.xml b/app/src/main/res/drawable/bg_input.xml deleted file mode 100644 index 1885ad6..0000000 --- a/app/src/main/res/drawable/bg_input.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/bg_send_button.xml b/app/src/main/res/drawable/bg_send_button.xml deleted file mode 100644 index 3c0ba1c..0000000 --- a/app/src/main/res/drawable/bg_send_button.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/bg_user_bubble.xml b/app/src/main/res/drawable/bg_user_bubble.xml deleted file mode 100644 index 814953c..0000000 --- a/app/src/main/res/drawable/bg_user_bubble.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 86ac5ac..0000000 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index bb70daa..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_models.xml b/app/src/main/res/layout/activity_models.xml deleted file mode 100644 index 62f35c4..0000000 --- a/app/src/main/res/layout/activity_models.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_settings.xml b/app/src/main/res/layout/dialog_settings.xml deleted file mode 100644 index e52cb50..0000000 --- a/app/src/main/res/layout/dialog_settings.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_chat_assistant.xml b/app/src/main/res/layout/item_chat_assistant.xml deleted file mode 100644 index 422f0bf..0000000 --- a/app/src/main/res/layout/item_chat_assistant.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/item_chat_user.xml b/app/src/main/res/layout/item_chat_user.xml deleted file mode 100644 index 85cdbee..0000000 --- a/app/src/main/res/layout/item_chat_user.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/item_model.xml b/app/src/main/res/layout/item_model.xml deleted file mode 100644 index 80aea68..0000000 --- a/app/src/main/res/layout/item_model.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml deleted file mode 100644 index a0a2294..0000000 --- a/app/src/main/res/layout/nav_header.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/menu/nav_menu.xml b/app/src/main/res/menu/nav_menu.xml deleted file mode 100644 index 0867dba..0000000 --- a/app/src/main/res/menu/nav_menu.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.xml b/app/src/main/res/mipmap-hdpi/ic_launcher.xml deleted file mode 100644 index f9890cc..0000000 --- a/app/src/main/res/mipmap-hdpi/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.xml b/app/src/main/res/mipmap-xhdpi/ic_launcher.xml deleted file mode 100644 index f9890cc..0000000 --- a/app/src/main/res/mipmap-xhdpi/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml b/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml deleted file mode 100644 index f9890cc..0000000 --- a/app/src/main/res/mipmap-xxhdpi/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml deleted file mode 100644 index a0c588c..0000000 --- a/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FFFFFFFF - #FF000000 - #FFF5F5F5 - #FF121212 - #FFE3F2FD - #FFFFFFFF - #FF212121 - #FF757575 - #FFE0E0E0 - - - #FF4A90D9 - #FF3A7BC8 - #FFFF6B35 - #FFF8F9FA - #FFFFFFFF - #FFE8F0FE - #FFF1F3F4 - #FFF0F2F5 - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index 2ba148f..0000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - MiMo Studio - diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml deleted file mode 100644 index fb4cb71..0000000 --- a/app/src/main/res/values/themes.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 2fb8337..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,5 +0,0 @@ -// Top-level build file -plugins { - id("com.android.application") version "8.7.3" apply false - id("org.jetbrains.kotlin.android") version "2.0.21" apply false -} diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index f0a2e55..0000000 --- a/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -android.useAndroidX=true -kotlin.code.style=official -android.nonTransitiveRClass=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index e2847c8..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index ceaef6e..0000000 --- a/gradlew +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# 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 -# -# https://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. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -app_path=$0 -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld -- "$app_path" ) - link=${ls#*' -> '} - case $link in - /*) app_path=$link ;; - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in - CYGWIN* ) cygwin=true ;; - Darwin* ) darwin=true ;; - MSYS* | MINGW* ) msys=true ;; - NonStop* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 ; then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - ;; - esac - case $MAX_FD in - '' | soft) :;; - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - ;; - esac -fi - -# Collect all arguments for the java command, stracks://am/LF://the current directory:// -# temporary://build://for://long://args:// -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - JAVACMD=$( cygpath --unix "$JAVACMD" ) -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and/or backslashes, so put them in -# temporary variables so that the://shell://can://split://them://up (https://github.com/gradle/gradle/issues/25036) -# * Put://all://options://into://a://single://quoted://word://so://they://are://not://split -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xeli" is available and://the://GRADLE_OPTS does not contain://any://-J flags -if ! "$cygwin" && ! "$msys" && [ -n "$TERM" ] ; then - : -fi - -exec "$JAVACMD" "$@" diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 7cd214b..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,16 +0,0 @@ -pluginManagement { - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} -dependencyResolution { - repositories { - google() - mavenCentral() - } -} - -rootProject.name = "MimoStudio" -include(":app")