mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-19 01:27:18 -04:00
Add icon to login account list (#595)
* Try to add icon to account * [pick account screen] add icon stage 2 TODO: extract skin head from skin * Autoscale head icon and try to fix layout * Remove import * Complete skin face extractor * (11 other fixes commits) Co-authored-by: Duy Tran Khanh <khanhduytran0@users.noreply.github.com>
This commit is contained in:
parent
21d5dfcef4
commit
416345b37d
BIN
app_pojavlauncher/src/main/assets/ic_steve.png
Normal file
BIN
app_pojavlauncher/src/main/assets/ic_steve.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 308 B |
@ -1,42 +1,75 @@
|
||||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.*;
|
||||
import android.Manifest;
|
||||
import android.app.Dialog;
|
||||
import android.content.*;
|
||||
import android.content.pm.*;
|
||||
import android.content.res.*;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.system.Os;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.*;
|
||||
import androidx.core.content.*;
|
||||
import androidx.appcompat.app.*;
|
||||
import android.system.*;
|
||||
import android.text.*;
|
||||
import android.text.style.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import android.widget.CompoundButton.*;
|
||||
import com.kdt.pickafile.*;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.authenticator.microsoft.*;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.*;
|
||||
import net.kdt.pojavlaunch.customcontrols.*;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
import net.kdt.pojavlaunch.utils.*;
|
||||
import org.apache.commons.compress.archivers.tar.*;
|
||||
import org.apache.commons.compress.compressors.xz.*;
|
||||
import org.apache.commons.io.*;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.kdt.pickafile.FileListView;
|
||||
import com.kdt.pickafile.FileSelectedListener;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.InvalidateTokenTask;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.LoginListener;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.LoginTask;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlData;
|
||||
import net.kdt.pojavlaunch.customcontrols.CustomControls;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.utils.JREUtils;
|
||||
import net.kdt.pojavlaunch.utils.LocaleUtils;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import net.kdt.pojavlaunch.value.*;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
public class PojavLoginActivity extends BaseActivity
|
||||
// MineActivity
|
||||
@ -683,10 +716,28 @@ public class PojavLoginActivity extends BaseActivity
|
||||
|
||||
for (String s : new File(Tools.DIR_ACCOUNT_NEW).list()) {
|
||||
View child = inflater.inflate(R.layout.simple_account_list_item, null);
|
||||
TextView accountName = child.findViewById(R.id.accountName);
|
||||
ImageButton removeButton = child.findViewById(R.id.removeBtn);
|
||||
ImageView accountIcon = child.findViewById(R.id.accountitem_image_icon);
|
||||
TextView accountName = child.findViewById(R.id.accountitem_text_name);
|
||||
ImageButton removeButton = child.findViewById(R.id.accountitem_button_remove);
|
||||
|
||||
accountName.setText(s.substring(0, s.length() - 5));
|
||||
String accNameStr = s.substring(0, s.length() - 5);
|
||||
String skinFaceBase64 = MinecraftAccount.load(accNameStr).skinFaceBase64;
|
||||
Bitmap bitmap = Bitmap.createBitmap(8, 8, Bitmap.Config.ARGB_8888);
|
||||
if (skinFaceBase64 != null) {
|
||||
byte[] faceIconBytes = Base64.decode(skinFaceBase64, Base64.DEFAULT);
|
||||
bitmap = BitmapFactory.decodeByteArray(faceIconBytes, 0, faceIconBytes.length);
|
||||
} else {
|
||||
try {
|
||||
bitmap = BitmapFactory.decodeStream(getAssets().open("ic_steve.png"));
|
||||
} catch (IOException e) {
|
||||
// Should never happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Bitmap upscaledBitmap = Bitmap.createScaledBitmap(bitmap, 80, 80, false);
|
||||
accountIcon.setImageBitmap(upscaledBitmap);
|
||||
|
||||
accountName.setText(accNameStr);
|
||||
|
||||
accountListLayout.addView(child);
|
||||
|
||||
@ -807,11 +858,14 @@ public class PojavLoginActivity extends BaseActivity
|
||||
builder.profileId = result[3];
|
||||
builder.username = result[4];
|
||||
builder.selectedVersion = "1.12.2";
|
||||
builder.updateSkinFace();
|
||||
mProfile = builder;
|
||||
}
|
||||
v.setEnabled(true);
|
||||
prb.setVisibility(View.GONE);
|
||||
playProfile(false);
|
||||
runOnUiThread(() -> {
|
||||
v.setEnabled(true);
|
||||
prb.setVisibility(View.GONE);
|
||||
playProfile(false);
|
||||
});
|
||||
}
|
||||
}).execute(edit2.getText().toString(), edit3.getText().toString());
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
|
||||
acc.profileId = msa.mcUuid;
|
||||
acc.isMicrosoft = true;
|
||||
acc.msaRefreshToken = msa.msRefreshToken;
|
||||
acc.updateSkinFace();
|
||||
}
|
||||
acc.save();
|
||||
|
||||
|
@ -6,7 +6,7 @@ import java.io.*;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
|
||||
public class LoginTask extends AsyncTask<String, Void, String[]>
|
||||
public class LoginTask extends AsyncTask<String, Void, Void>
|
||||
{
|
||||
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
|
||||
//private String TAG = "MojangAuth-login";
|
||||
@ -29,7 +29,7 @@ public class LoginTask extends AsyncTask<String, Void, String[]>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(String[] args) {
|
||||
protected Void doInBackground(String[] args) {
|
||||
ArrayList<String> str = new ArrayList<String>();
|
||||
str.add("ERROR");
|
||||
try{
|
||||
@ -57,12 +57,15 @@ public class LoginTask extends AsyncTask<String, Void, String[]>
|
||||
catch(Exception e){
|
||||
str.add(e.getMessage());
|
||||
}
|
||||
return str.toArray(new String[0]);
|
||||
|
||||
listener.onLoginDone(str.toArray(new String[0]));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String[] result) {
|
||||
listener.onLoginDone(result);
|
||||
protected void onPostExecute(Void result) {
|
||||
// listener.onLoginDone(result);
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
||||
this.profilePath = MinecraftAccount.load(args[0]);
|
||||
int responseCode = 400;
|
||||
responseCode = this.authenticator.validate(profilePath.accessToken).statusCode;
|
||||
if (responseCode >= 200 && responseCode < 300) {
|
||||
if (responseCode == 403) {
|
||||
RefreshResponse response = this.authenticator.refresh(profilePath.accessToken, UUID.fromString(profilePath.clientToken));
|
||||
// if (response == null) {
|
||||
// throw new NullPointerException("Response is null?");
|
||||
@ -54,8 +54,9 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
||||
profilePath.accessToken = response.accessToken;
|
||||
profilePath.username = response.selectedProfile.name;
|
||||
profilePath.profileId = response.selectedProfile.id;
|
||||
profilePath.save();
|
||||
}
|
||||
profilePath.updateSkinFace();
|
||||
profilePath.save();
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
return e;
|
||||
|
@ -0,0 +1,51 @@
|
||||
package net.kdt.pojavlaunch.value;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Base64;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
|
||||
public class AccountSkin {
|
||||
public static Bitmap getSkin(String uuid) throws IOException {
|
||||
Profile p = Tools.GLOBAL_GSON.fromJson(DownloadUtils.downloadString("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid), Profile.class);
|
||||
for (Property property : p.properties) {
|
||||
if (property.name.equals("textures")) {
|
||||
return getSkinFromProperty(Tools.GLOBAL_GSON.fromJson(new String(Base64.decode(property.value, Base64.DEFAULT), "UTF-8"), SkinProperty.class));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Bitmap getSkinFromProperty(SkinProperty p) throws IOException {
|
||||
for (Map.Entry<String, Texture> texture : p.textures.entrySet()) {
|
||||
if (texture.getKey().equals("SKIN")) {
|
||||
String skinFile = File.createTempFile("skin", "png", new File(Tools.DIR_DATA, "cache")).getAbsolutePath();
|
||||
Tools.downloadFile(texture.getValue().url, skinFile);
|
||||
return BitmapFactory.decodeFile(skinFile);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class Texture {
|
||||
public String url;
|
||||
}
|
||||
|
||||
public static class SkinProperty {
|
||||
public Map<String, Texture> textures;
|
||||
}
|
||||
|
||||
public static class Property {
|
||||
public String name, value;
|
||||
}
|
||||
|
||||
public static class Profile {
|
||||
public Property[] properties;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ import android.util.Log;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import java.io.*;
|
||||
import com.google.gson.*;
|
||||
import android.os.Environment;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.util.Base64;
|
||||
|
||||
public class MinecraftAccount
|
||||
{
|
||||
@ -14,6 +18,34 @@ public class MinecraftAccount
|
||||
public String selectedVersion = "1.7.10";
|
||||
public boolean isMicrosoft = false;
|
||||
public String msaRefreshToken = "0";
|
||||
public String skinFaceBase64;
|
||||
|
||||
public void updateSkinFace() {
|
||||
try {
|
||||
Bitmap bSkin = AccountSkin.getSkin(profileId);
|
||||
if (bSkin.getWidth() != 64 || bSkin.getHeight() != 64) {
|
||||
Log.w("SkinLoader", "Only skin size 64x64 is currently supported, this skin is " + bSkin.getWidth() + "x" + bSkin.getHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
int[] pixels = new int[8 * 8];
|
||||
bSkin.getPixels(pixels, 0, 8, 8, 8, 8, 8);
|
||||
bSkin.recycle();
|
||||
|
||||
ByteArrayOutputStream outByteArr = new ByteArrayOutputStream();
|
||||
Bitmap bFace = Bitmap.createBitmap(pixels, 8, 8, Bitmap.Config.ARGB_8888);
|
||||
bFace.compress(Bitmap.CompressFormat.PNG, 100, outByteArr);
|
||||
bFace.recycle();
|
||||
skinFaceBase64 = Base64.encodeToString(outByteArr.toByteArray(), Base64.DEFAULT);
|
||||
outByteArr.close();
|
||||
|
||||
Log.i("SkinLoader", "Update skin face success");
|
||||
} catch (IOException e) {
|
||||
// Skin refresh limit, no internet connection, etc...
|
||||
// Simply ignore updating skin face
|
||||
Log.w("SkinLoader", "Could not update skin face", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String save(String outPath) throws IOException {
|
||||
Tools.write(outPath, Tools.GLOBAL_GSON.toJson(this));
|
||||
@ -50,7 +82,7 @@ public class MinecraftAccount
|
||||
acc.msaRefreshToken = "0";
|
||||
}
|
||||
return acc;
|
||||
}catch(IOException e) {
|
||||
} catch(IOException e) {
|
||||
Log.e(MinecraftAccount.class.getName(), "Caught an exception while loading the profile",e);
|
||||
return null;
|
||||
}
|
||||
|
@ -6,8 +6,19 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/accountitem_image_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accountName"
|
||||
android:id="@+id/accountitem_text_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
@ -18,22 +29,23 @@
|
||||
|
||||
android:text="Name Placeholder"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@+id/removeBtn"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@id/accountitem_image_icon"
|
||||
app:layout_constraintRight_toLeftOf="@id/accountitem_button_remove"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/removeBtn"
|
||||
android:id="@+id/accountitem_button_remove"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingStart="10dp"
|
||||
|
||||
android:paddingEnd="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/accountName"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_remove" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user