@@ -7,6 +7,7 @@ import android.graphics.Bitmap
77import android.graphics.BitmapFactory
88import android.os.Bundle
99import android.provider.MediaStore
10+ import android.util.Log
1011import android.view.View
1112import android.widget.*
1213import androidx.appcompat.app.AlertDialog
@@ -19,7 +20,7 @@ import java.net.URL
1920
2021class MainActivity : Activity () {
2122
22- companion object { private const val TAG = " SkinDebug " }
23+ companion object { private const val TAG = " SkinUpload " }
2324
2425 private lateinit var txtUsername: TextView
2526 private lateinit var btnSelect: Button
@@ -55,27 +56,24 @@ class MainActivity : Activity() {
5556
5657 setupUI()
5758 checkLogin()
58-
59- // ★ 初期スキンは res/raw/steve.png
6059 loadDefaultSteveSkin()
6160 }
6261
63- /* * res/raw/steve.png を読み込む */
64- private fun loadDefaultSteveSkin () {
65- val input = resources.openRawResource(R .raw.steve)
66- val bmp = BitmapFactory .decodeStream(input)
67- val resized = resizeTo64(bmp)
68-
69- currentSkinBitmap = resized
70- skinView.setImageBitmap(resized)
71- }
72-
7362 private fun setupUI () {
7463 btnSelect.backgroundTintList = ColorStateList .valueOf(colorSelect)
7564 btnUpload.backgroundTintList = ColorStateList .valueOf(colorUploadInitial)
7665 btnUpload.visibility = View .GONE
7766 progressBar.visibility = View .GONE
7867
68+ // ★ 文字を白色に統一
69+ val white = getColor(R .color.white)
70+ txtUsername.setTextColor(white)
71+ lblModel.setTextColor(white)
72+ btnSelect.setTextColor(white)
73+ btnUpload.setTextColor(white)
74+ btnLibrary.setTextColor(white)
75+ btnLogout.setTextColor(white)
76+
7977 switchModel.setOnCheckedChangeListener { _, isChecked ->
8078 lblModel.text = if (isChecked) " モデル: Alex" else " モデル: Steve"
8179 }
@@ -84,10 +82,7 @@ class MainActivity : Activity() {
8482 btnUpload.setOnClickListener { handleUpload() }
8583
8684 btnLibrary.setOnClickListener {
87- AlertDialog .Builder (this )
88- .setMessage(" 未実装" )
89- .setPositiveButton(" OK" , null )
90- .show()
85+ AlertDialog .Builder (this ).setMessage(" 未実装" ).setPositiveButton(" OK" , null ).show()
9186 }
9287
9388 btnLogout.setOnClickListener {
@@ -97,10 +92,19 @@ class MainActivity : Activity() {
9792 }
9893 }
9994
95+ /* * res/raw/steve.png 読み込み */
96+ private fun loadDefaultSteveSkin () {
97+ val input = resources.openRawResource(R .raw.steve)
98+ val bmp = BitmapFactory .decodeStream(input)
99+ currentSkinBitmap = resizeTo64(bmp)
100+ skinView.setImageBitmap(currentSkinBitmap)
101+ }
102+
100103 private fun checkLogin () {
101- val prefs = getSharedPreferences(" prefs" , MODE_PRIVATE )
102- val username = prefs.getString(" minecraft_username" , null )
103- val token = prefs.getString(" minecraft_token" , null )
104+ val p = getSharedPreferences(" prefs" , MODE_PRIVATE )
105+ val username = p.getString(" minecraft_username" , null )
106+ val token = p.getString(" minecraft_token" , null )
107+
104108 if (username == null || token == null ) {
105109 startActivity(Intent (this , WelcomeActivity ::class .java))
106110 finish()
@@ -109,7 +113,6 @@ class MainActivity : Activity() {
109113 }
110114 }
111115
112- /* * ファイル選択 */
113116 private fun selectSkinImage () {
114117 val intent = Intent (Intent .ACTION_GET_CONTENT ).apply { type = " image/*" }
115118 startActivityForResult(Intent .createChooser(intent, " スキンを選択" ), REQUEST_SKIN_PICK )
@@ -119,7 +122,6 @@ class MainActivity : Activity() {
119122 super .onActivityResult(req, res, data)
120123 if (req == REQUEST_SKIN_PICK && res == Activity .RESULT_OK ) {
121124 val uri = data?.data ? : return
122-
123125 try {
124126 val orig = MediaStore .Images .Media .getBitmap(contentResolver, uri)
125127 val bmp = resizeTo64(orig)
@@ -133,8 +135,7 @@ class MainActivity : Activity() {
133135 } catch (e: Exception ) {
134136 e.printStackTrace()
135137 AlertDialog .Builder (this )
136- .setTitle(" エラー" )
137- .setMessage(" スキンの読み込みに失敗しました: ${e.message} " )
138+ .setMessage(" スキンの読み込み失敗: ${e.message} " )
138139 .setPositiveButton(" OK" , null )
139140 .show()
140141 }
@@ -150,43 +151,38 @@ class MainActivity : Activity() {
150151 )
151152 }
152153
153- /* * アップロード */
154+ /* * 実行ボタン */
154155 private fun handleUpload () {
155- val bmp = currentSkinBitmap ? : run {
156- Toast .makeText(this , " スキンが選択されていません" , Toast .LENGTH_SHORT ).show()
157- return
158- }
159-
156+ val bmp = currentSkinBitmap ? : return
160157 val prefs = getSharedPreferences(" prefs" , MODE_PRIVATE )
161- val mcToken = prefs.getString(" minecraft_token" , null ) ? : return
162- val modelType = if (switchModel.isChecked) " slim" else " classic"
158+ val token = prefs.getString(" minecraft_token" , null ) ? : return
159+ val model = if (switchModel.isChecked) " slim" else " classic"
163160
164161 progressBar.visibility = View .VISIBLE
165162 progressBar.progress = 0
166163
167164 scope.launch {
168- val ok = uploadSkin(mcToken, bmp, modelType) { progress ->
169- progressBar.progress = progress
170- }
171-
165+ val ok = uploadSkin(token, bmp, model) { progressBar.progress = it }
172166 progressBar.visibility = View .GONE
167+
173168 Toast .makeText(
174169 this @MainActivity,
175- if (ok) " アップロード完了 " else " アップロード失敗" ,
176- Toast .LENGTH_SHORT
170+ if (ok) " アップロード成功 " else " アップロード失敗(ログ確認) " ,
171+ Toast .LENGTH_LONG
177172 ).show()
178173 }
179174 }
180175
181- /* * Mojang API 正式対応のスキンアップロード */
176+ /* * 本物の Mojang API 仕様に合わせた multipart */
182177 private suspend fun uploadSkin (
183178 token : String ,
184179 bmp : Bitmap ,
185180 model : String ,
186181 onProgress : (Int ) -> Unit
187182 ): Boolean = withContext(Dispatchers .IO ) {
183+
188184 try {
189- val boundary = " ----RabimiSkinBoundary "
185+ val boundary = " ----RabimiBoundary "
190186 val url = URL (" https://api.minecraftservices.com/minecraft/profile/skins" )
191187 val conn = url.openConnection() as HttpURLConnection
192188
@@ -199,47 +195,58 @@ class MainActivity : Activity() {
199195
200196 val out = DataOutputStream (conn.outputStream)
201197
202- // skinModel
198+ Log .d(TAG , " variant = $model " )
199+
200+ // ★ variant が正しいキー名(skinModel ではない)
203201 out .writeBytes(" --$boundary \r\n " )
204- out .writeBytes(" Content-Disposition: form-data; name=\" skinModel \"\r\n\r\n " )
202+ out .writeBytes(" Content-Disposition: form-data; name=\" variant \"\r\n\r\n " )
205203 out .writeBytes(" $model \r\n " )
206204
207- // file
205+ // PNG file
208206 out .writeBytes(" --$boundary \r\n " )
209207 out .writeBytes(" Content-Disposition: form-data; name=\" file\" ; filename=\" skin.png\"\r\n " )
210208 out .writeBytes(" Content-Type: image/png\r\n\r\n " )
211209
212- val pngBaos = ByteArrayOutputStream ()
213- bmp.compress(Bitmap .CompressFormat .PNG , 100 , pngBaos)
214- val bytes = pngBaos.toByteArray()
215-
216- val chunk = (bytes.size / 100 ).coerceAtLeast(1 )
210+ val png = ByteArrayOutputStream ().apply {
211+ bmp.compress(Bitmap .CompressFormat .PNG , 100 , this )
212+ }.toByteArray()
217213
214+ val chunk = (png.size / 100 ).coerceAtLeast(1 )
218215 var written = 0
216+
219217 for (i in 0 until 100 ) {
220218 val start = i * chunk
221- if (start >= bytes .size) break
222- val end = ((i + 1 ) * chunk).coerceAtMost(bytes .size)
223- out .write(bytes , start, end - start)
219+ if (start >= png .size) break
220+ val end = ((i + 1 ) * chunk).coerceAtMost(png .size)
221+ out .write(png , start, end - start)
224222 written = end
225223 onProgress(i + 1 )
226224 }
227225
228- // 残り書き込み
229- if (written < bytes.size) {
230- out .write(bytes, written, bytes.size - written)
226+ if (written < png.size) {
227+ out .write(png, written, png.size - written)
231228 onProgress(100 )
232229 }
233230
234231 out .writeBytes(" \r\n --$boundary --\r\n " )
235232 out .flush()
236233 out .close()
237234
238- val rc = conn.responseCode
239- rc in 200 .. 299
235+ val code = conn.responseCode
236+ Log .d(TAG , " HTTP code = $code " )
237+
238+ if (code !in 200 .. 299 ) {
239+ val err = conn.errorStream?.bufferedReader()?.readText()
240+ Log .e(TAG , " Error response: $err " )
241+ } else {
242+ val ok = conn.inputStream.bufferedReader().readText()
243+ Log .d(TAG , " Success: $ok " )
244+ }
245+
246+ code in 200 .. 299
240247
241248 } catch (e: Exception ) {
242- e.printStackTrace( )
249+ Log .e( TAG , " UPLOAD ERROR " , e )
243250 false
244251 }
245252 }
0 commit comments