mirror of
https://github.com/2009scape/2009Scape-mobile.git
synced 2025-12-21 09:01:56 -07: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
9 changed files with 205 additions and 51 deletions
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;
|
package net.kdt.pojavlaunch;
|
||||||
|
|
||||||
import android.*;
|
import android.Manifest;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.*;
|
import android.content.DialogInterface;
|
||||||
import android.content.pm.*;
|
import android.content.Intent;
|
||||||
import android.content.res.*;
|
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.Color;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.*;
|
import android.net.Uri;
|
||||||
import android.os.*;
|
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.annotation.NonNull;
|
||||||
import androidx.core.app.*;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.*;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.appcompat.app.*;
|
import androidx.core.content.ContextCompat;
|
||||||
import android.system.*;
|
import com.kdt.pickafile.FileListView;
|
||||||
import android.text.*;
|
import com.kdt.pickafile.FileSelectedListener;
|
||||||
import android.text.style.*;
|
import java.io.BufferedInputStream;
|
||||||
import android.util.*;
|
import java.io.BufferedReader;
|
||||||
import android.view.*;
|
import java.io.File;
|
||||||
import android.widget.*;
|
import java.io.FileInputStream;
|
||||||
import android.widget.CompoundButton.*;
|
import java.io.FileOutputStream;
|
||||||
import com.kdt.pickafile.*;
|
import java.io.IOException;
|
||||||
import java.io.*;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.io.InputStreamReader;
|
||||||
import net.kdt.pojavlaunch.authenticator.microsoft.*;
|
import java.util.Arrays;
|
||||||
import net.kdt.pojavlaunch.authenticator.mojang.*;
|
import java.util.Locale;
|
||||||
import net.kdt.pojavlaunch.customcontrols.*;
|
import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask;
|
||||||
import net.kdt.pojavlaunch.prefs.*;
|
import net.kdt.pojavlaunch.authenticator.mojang.InvalidateTokenTask;
|
||||||
import net.kdt.pojavlaunch.utils.*;
|
import net.kdt.pojavlaunch.authenticator.mojang.LoginListener;
|
||||||
import org.apache.commons.compress.archivers.tar.*;
|
import net.kdt.pojavlaunch.authenticator.mojang.LoginTask;
|
||||||
import org.apache.commons.compress.compressors.xz.*;
|
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
|
||||||
import org.apache.commons.io.*;
|
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 org.apache.commons.io.FileUtils;
|
||||||
import net.kdt.pojavlaunch.value.*;
|
import org.apache.commons.io.IOUtils;
|
||||||
import com.google.gson.*;
|
|
||||||
|
|
||||||
public class PojavLoginActivity extends BaseActivity
|
public class PojavLoginActivity extends BaseActivity
|
||||||
// MineActivity
|
// MineActivity
|
||||||
|
|
@ -683,10 +716,28 @@ public class PojavLoginActivity extends BaseActivity
|
||||||
|
|
||||||
for (String s : new File(Tools.DIR_ACCOUNT_NEW).list()) {
|
for (String s : new File(Tools.DIR_ACCOUNT_NEW).list()) {
|
||||||
View child = inflater.inflate(R.layout.simple_account_list_item, null);
|
View child = inflater.inflate(R.layout.simple_account_list_item, null);
|
||||||
TextView accountName = child.findViewById(R.id.accountName);
|
ImageView accountIcon = child.findViewById(R.id.accountitem_image_icon);
|
||||||
ImageButton removeButton = child.findViewById(R.id.removeBtn);
|
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);
|
accountListLayout.addView(child);
|
||||||
|
|
||||||
|
|
@ -807,11 +858,14 @@ public class PojavLoginActivity extends BaseActivity
|
||||||
builder.profileId = result[3];
|
builder.profileId = result[3];
|
||||||
builder.username = result[4];
|
builder.username = result[4];
|
||||||
builder.selectedVersion = "1.12.2";
|
builder.selectedVersion = "1.12.2";
|
||||||
|
builder.updateSkinFace();
|
||||||
mProfile = builder;
|
mProfile = builder;
|
||||||
}
|
}
|
||||||
v.setEnabled(true);
|
runOnUiThread(() -> {
|
||||||
prb.setVisibility(View.GONE);
|
v.setEnabled(true);
|
||||||
playProfile(false);
|
prb.setVisibility(View.GONE);
|
||||||
|
playProfile(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).execute(edit2.getText().toString(), edit3.getText().toString());
|
}).execute(edit2.getText().toString(), edit3.getText().toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
|
||||||
acc.profileId = msa.mcUuid;
|
acc.profileId = msa.mcUuid;
|
||||||
acc.isMicrosoft = true;
|
acc.isMicrosoft = true;
|
||||||
acc.msaRefreshToken = msa.msRefreshToken;
|
acc.msaRefreshToken = msa.msRefreshToken;
|
||||||
|
acc.updateSkinFace();
|
||||||
}
|
}
|
||||||
acc.save();
|
acc.save();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import net.kdt.pojavlaunch.*;
|
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 YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
|
||||||
//private String TAG = "MojangAuth-login";
|
//private String TAG = "MojangAuth-login";
|
||||||
|
|
@ -29,7 +29,7 @@ public class LoginTask extends AsyncTask<String, Void, String[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] doInBackground(String[] args) {
|
protected Void doInBackground(String[] args) {
|
||||||
ArrayList<String> str = new ArrayList<String>();
|
ArrayList<String> str = new ArrayList<String>();
|
||||||
str.add("ERROR");
|
str.add("ERROR");
|
||||||
try{
|
try{
|
||||||
|
|
@ -57,12 +57,15 @@ public class LoginTask extends AsyncTask<String, Void, String[]>
|
||||||
catch(Exception e){
|
catch(Exception e){
|
||||||
str.add(e.getMessage());
|
str.add(e.getMessage());
|
||||||
}
|
}
|
||||||
return str.toArray(new String[0]);
|
|
||||||
|
listener.onLoginDone(str.toArray(new String[0]));
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(String[] result) {
|
protected void onPostExecute(Void result) {
|
||||||
listener.onLoginDone(result);
|
// listener.onLoginDone(result);
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
||||||
this.profilePath = MinecraftAccount.load(args[0]);
|
this.profilePath = MinecraftAccount.load(args[0]);
|
||||||
int responseCode = 400;
|
int responseCode = 400;
|
||||||
responseCode = this.authenticator.validate(profilePath.accessToken).statusCode;
|
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));
|
RefreshResponse response = this.authenticator.refresh(profilePath.accessToken, UUID.fromString(profilePath.clientToken));
|
||||||
// if (response == null) {
|
// if (response == null) {
|
||||||
// throw new NullPointerException("Response is null?");
|
// throw new NullPointerException("Response is null?");
|
||||||
|
|
@ -54,8 +54,9 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
||||||
profilePath.accessToken = response.accessToken;
|
profilePath.accessToken = response.accessToken;
|
||||||
profilePath.username = response.selectedProfile.name;
|
profilePath.username = response.selectedProfile.name;
|
||||||
profilePath.profileId = response.selectedProfile.id;
|
profilePath.profileId = response.selectedProfile.id;
|
||||||
profilePath.save();
|
|
||||||
}
|
}
|
||||||
|
profilePath.updateSkinFace();
|
||||||
|
profilePath.save();
|
||||||
return null;
|
return null;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
return 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 net.kdt.pojavlaunch.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
public class MinecraftAccount
|
public class MinecraftAccount
|
||||||
{
|
{
|
||||||
|
|
@ -14,6 +18,34 @@ public class MinecraftAccount
|
||||||
public String selectedVersion = "1.7.10";
|
public String selectedVersion = "1.7.10";
|
||||||
public boolean isMicrosoft = false;
|
public boolean isMicrosoft = false;
|
||||||
public String msaRefreshToken = "0";
|
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 {
|
public String save(String outPath) throws IOException {
|
||||||
Tools.write(outPath, Tools.GLOBAL_GSON.toJson(this));
|
Tools.write(outPath, Tools.GLOBAL_GSON.toJson(this));
|
||||||
|
|
@ -50,7 +82,7 @@ public class MinecraftAccount
|
||||||
acc.msaRefreshToken = "0";
|
acc.msaRefreshToken = "0";
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}catch(IOException e) {
|
} catch(IOException e) {
|
||||||
Log.e(MinecraftAccount.class.getName(), "Caught an exception while loading the profile",e);
|
Log.e(MinecraftAccount.class.getName(), "Caught an exception while loading the profile",e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,19 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="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
|
<TextView
|
||||||
android:id="@+id/accountName"
|
android:id="@+id/accountitem_text_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
|
@ -18,22 +29,23 @@
|
||||||
|
|
||||||
android:text="Name Placeholder"
|
android:text="Name Placeholder"
|
||||||
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/removeBtn"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintLeft_toRightOf="@id/accountitem_image_icon"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/accountitem_button_remove"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/removeBtn"
|
android:id="@+id/accountitem_button_remove"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:paddingStart="10dp"
|
android:paddingStart="10dp"
|
||||||
|
|
||||||
android:paddingEnd="10dp"
|
android:paddingEnd="10dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/accountName"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:srcCompat="@drawable/ic_remove" />
|
app:srcCompat="@drawable/ic_remove" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue