Mojang + Offline account removal

Offline account is now Local account
This commit is contained in:
khanhduytran0 2022-03-11 20:05:59 +07:00
parent de8a16e5c5
commit deef6223d3
16 changed files with 35 additions and 347 deletions

View File

@ -108,3 +108,4 @@ Any code change should be submitted as a pull request. The description should ex
- [xHook](https://github.com/iqiyi/xHook) (Used for exit code trapping): [MIT and BSD-style licenses](https://github.com/iqiyi/xHook/blob/master/LICENSE).
- [libepoxy](https://github.com/anholt/libepoxy): [MIT License](https://github.com/anholt/libepoxy/blob/master/COPYING).
- [virglrenderer](https://github.com/PojavLauncherTeam/virglrenderer): [MIT License](https://gitlab.freedesktop.org/virgl/virglrenderer/-/blob/master/COPYING).
- Thanks to [MCHeads](https://mc-heads.net) for providing Minecraft avatars.

View File

@ -99,7 +99,22 @@ public abstract class BaseLauncherActivity extends BaseActivity {
} else if (canBack) {
v.setEnabled(false);
mTask = new MinecraftDownloaderTask(this);
mTask.execute(mProfile.selectedVersion);
// TODO: better check!!!
if (mProfile.accessToken.equals("0")) {
File verJsonFile = new File(Tools.DIR_HOME_VERSION,
mProfile.selectedVersion + "/" + mProfile.selectedVersion + ".json");
if (verJsonFile.exists()) {
mTask.onPostExecute(null);
} else {
new AlertDialog.Builder(this)
.setTitle(R.string.global_error)
.setMessage(R.string.mcl_launch_error_localmode)
.setPositiveButton(android.R.string.ok, null)
.show();
}
} else {
mTask.execute(mProfile.selectedVersion);
}
}
}

View File

@ -228,7 +228,7 @@ public class PojavLauncherActivity extends BaseLauncherActivity
accountFaceImageView.setImageBitmap(mProfile.getSkinFace());
//TODO FULL BACKGROUND LOGIN
tvConnectStatus.setText(mProfile.accessToken.equals("0") ? R.string.mcl_account_offline : R.string.mcl_account_connected);
tvConnectStatus.setText(mProfile.accessToken.equals("0") ? R.string.mcl_account_local : R.string.mcl_account_connected);
} catch(Exception e) {
mProfile = new MinecraftAccount();
Tools.showError(this, e, true);

View File

@ -43,9 +43,6 @@ import androidx.core.content.ContextCompat;
import net.kdt.pojavlaunch.authenticator.microsoft.MicrosoftAuthTask;
import net.kdt.pojavlaunch.authenticator.microsoft.ui.MicrosoftLoginGUIActivity;
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.CustomControls;
import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog;
@ -69,9 +66,9 @@ public class PojavLoginActivity extends BaseActivity {
private final Object mLockStoragePerm = new Object();
private final Object mLockSelectJRE = new Object();
private EditText edit2, edit3;
private EditText edit2;
private final int REQUEST_STORAGE_REQUEST_CODE = 1;
private CheckBox sRemember, sOffline;
private CheckBox sRemember;
private TextView startupTextView;
private SharedPreferences firstLaunchPrefs;
private MinecraftAccount mProfile = null;
@ -237,15 +234,8 @@ public class PojavLoginActivity extends BaseActivity {
});
edit2 = (EditText) findViewById(R.id.login_edit_email);
edit3 = (EditText) findViewById(R.id.login_edit_password);
sRemember = findViewById(R.id.login_switch_remember);
sOffline = findViewById(R.id.login_switch_offline);
sOffline.setOnCheckedChangeListener((p1, p2) -> {
// May delete later
edit3.setEnabled(!p2);
});
isSkipInit = true;
}
@ -529,11 +519,9 @@ public class PojavLoginActivity extends BaseActivity {
};
MinecraftAccount acc = MinecraftAccount.load(selectedAccName);
if (acc.isMicrosoft){
if (acc.accessToken.length() >= 5){
new MicrosoftAuthTask(PojavLoginActivity.this, authListener)
.execute("true", acc.msaRefreshToken);
} else if (acc.accessToken.length() >= 5) {
PojavProfile.updateTokens(PojavLoginActivity.this, selectedAccName, authListener);
} else {
accountDialog.dismiss();
PojavProfile.launch(PojavLoginActivity.this, selectedAccName);
@ -574,7 +562,7 @@ public class PojavLoginActivity extends BaseActivity {
accountDialog.show();
}
private MinecraftAccount loginOffline() {
private MinecraftAccount loginLocal() {
new File(Tools.DIR_ACCOUNT_OLD).mkdir();
String text = edit2.getText().toString();
@ -584,8 +572,6 @@ public class PojavLoginActivity extends BaseActivity {
edit2.setError(getString(R.string.login_error_invalid_username));
} else if (new File(Tools.DIR_ACCOUNT_NEW + "/" + text + ".json").exists()) {
edit2.setError(getString(R.string.login_error_exist_username));
} else if (!edit3.getText().toString().isEmpty()) {
edit3.setError(getString(R.string.login_error_offline_password));
} else {
MinecraftAccount builder = new MinecraftAccount();
builder.isMicrosoft = false;
@ -599,43 +585,8 @@ public class PojavLoginActivity extends BaseActivity {
public void loginMC(final View v)
{
if (sOffline.isChecked()) {
mProfile = loginOffline();
playProfile(false);
} else {
ProgressBar prb = findViewById(R.id.launcherAccProgress);
new LoginTask().setLoginListener(new LoginListener(){
@Override
public void onBeforeLogin() {
v.setEnabled(false);
prb.setVisibility(View.VISIBLE);
}
@Override
public void onLoginDone(String[] result) {
if(result[0].equals("ERROR")){
Tools.dialogOnUiThread(PojavLoginActivity.this,
getResources().getString(R.string.global_error), strArrToString(result));
} else{
MinecraftAccount builder = new MinecraftAccount();
builder.accessToken = result[1];
builder.clientToken = result[2];
builder.profileId = result[3];
builder.username = result[4];
builder.updateSkinFace();
mProfile = builder;
}
runOnUiThread(() -> {
v.setEnabled(true);
prb.setVisibility(View.GONE);
playProfile(false);
});
}
}).execute(edit2.getText().toString(), edit3.getText().toString());
}
mProfile = loginLocal();
playProfile(false);
}
private void playProfile(boolean notOnLogin) {

View File

@ -8,8 +8,6 @@ import android.util.Log;
import com.google.gson.JsonSyntaxException;
import java.io.File;
import java.io.IOException;
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
import net.kdt.pojavlaunch.authenticator.mojang.RefreshTokenTask;
import net.kdt.pojavlaunch.value.MinecraftAccount;
public class PojavProfile {
@ -103,8 +101,4 @@ public class PojavProfile {
Intent intent = new Intent(ctx, PojavLauncherActivity.class); //MCLauncherActivity.class);
ctx.startActivity(intent);
}
public static void updateTokens(final Activity ctx, final String name, RefreshListener listen) throws Exception {
new RefreshTokenTask(ctx, listen).execute(name);
}
}

View File

@ -197,7 +197,7 @@ public final class Tools {
versionName = versionInfo.inheritsFrom;
}
String userType = "mojang";
String userType = "msa";
File gameDir = new File(strGameDir);
gameDir.mkdirs();

View File

@ -1,7 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang;
public interface LoginListener
{
public void onBeforeLogin();
public void onLoginDone(String[] result);
}

View File

@ -1,71 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang;
import android.os.*;
import net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.*;
import java.io.*;
import java.util.*;
import net.kdt.pojavlaunch.*;
public class LoginTask extends AsyncTask<String, Void, Void>
{
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
//private String TAG = "MojangAuth-login";
private LoginListener listener;
public LoginTask setLoginListener(LoginListener listener) {
this.listener = listener;
return this;
}
private UUID getRandomUUID() {
return UUID.randomUUID();
}
@Override
protected void onPreExecute() {
listener.onBeforeLogin();
super.onPreExecute();
}
@Override
protected Void doInBackground(String[] args) {
ArrayList<String> str = new ArrayList<String>();
str.add("ERROR");
try{
try{
AuthenticateResponse response = authenticator.authenticate(args[0], args[1], getRandomUUID());
if (response.selectedProfile == null) {
str.add("Can't login a demo account!\n");
} else {
if (new File(Tools.DIR_ACCOUNT_NEW + "/" + response.selectedProfile.name + ".json").exists()) {
str.add("This account already exist!\n");
} else {
str.add(response.accessToken); // Access token
str.add(response.clientToken.toString()); // Client token
str.add(response.selectedProfile.id); // Profile ID
str.add(response.selectedProfile.name); // Username
str.set(0, "NORMAL");
}
}
}
//MainActivity.updateStatus(804);
catch(Throwable e){
str.add(e.getMessage());
}
}
catch(Exception e){
str.add(e.getMessage());
}
listener.onLoginDone(str.toArray(new String[0]));
return null;
}
@Override
protected void onPostExecute(Void result) {
// listener.onLoginDone(result);
super.onPostExecute(result);
}
}

View File

@ -1,79 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang;
import android.content.*;
import android.os.*;
import com.google.gson.*;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
import net.kdt.pojavlaunch.*;
import net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.*;
import android.app.*;
import net.kdt.pojavlaunch.value.*;
public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
//private Gson gson = new Gson();
private RefreshListener listener;
private MinecraftAccount profilePath;
private final WeakReference<Context> ctx;
private ProgressDialog build;
public RefreshTokenTask(Context ctx, RefreshListener listener) {
this.ctx = new WeakReference<>(ctx);
this.listener = listener;
}
@Override
public void onPreExecute() {
build = new ProgressDialog(ctx.get());
build.setMessage(ctx.get().getString(R.string.global_waiting));
build.setProgressStyle(ProgressDialog.STYLE_SPINNER);
build.setCancelable(false);
build.show();
}
@Override
public Throwable doInBackground(String... args) {
try {
this.profilePath = MinecraftAccount.load(args[0]);
int responseCode = 400;
try {
responseCode = this.authenticator.validate(profilePath.accessToken).statusCode;
}catch(RuntimeException e) {}
if (responseCode == 403) {
RefreshResponse response = this.authenticator.refresh(profilePath.accessToken, UUID.fromString(profilePath.clientToken));
if (response == null) {
// Refresh when offline?
return null;
} else if (response.selectedProfile == null) {
throw new IllegalArgumentException("Can't refresh a demo account!");
}
profilePath.clientToken = response.clientToken.toString();
profilePath.accessToken = response.accessToken;
profilePath.username = response.selectedProfile.name;
profilePath.profileId = response.selectedProfile.id;
}
profilePath.updateSkinFace();
profilePath.save();
return null;
} catch (Throwable e) {
return e;
}
}
@Override
public void onPostExecute(Throwable result) {
build.dismiss();
if (result == null) {
listener.onSuccess(profilePath);
} else {
listener.onFailed(result);
}
}
}

View File

@ -1,24 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class AuthenticateRequest {
public AgentInfo agent = new AgentInfo();
public UUID clientToken;
public String password;
public String username;
public static class AgentInfo {
public String name;
public int version;
}
public AuthenticateRequest(String username, String password, UUID clientToken, String clientName, int clientVersion) {
this.username = username;
this.password = password;
this.clientToken = clientToken;
this.agent.name = clientName;
this.agent.version = clientVersion;
}
}

View File

@ -1,11 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class AuthenticateResponse {
public String accessToken;
public Profile[] availableProfiles;
public UUID clientToken;
public Profile selectedProfile;
}

View File

@ -1,10 +0,0 @@
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
import java.util.UUID;
public class RefreshResponse {
public String accessToken;
public UUID clientToken;
public Profile selectedProfile;
}

View File

@ -90,31 +90,6 @@ public class YggdrasilAuthenticator {
}
}
public AuthenticateResponse authenticate(String username, String password, UUID clientId) throws IOException, Throwable {
NetworkResponse obj = makeRequest("authenticate", new AuthenticateRequest(username, password, clientId, this.clientName, this.clientVersion), AuthenticateResponse.class);
/*
if (obj.statusCode != 200) {
throw new RuntimeException("Invalid username or password, status code: " + obj.statusCode);
}
*/
obj.throwExceptionIfNeed();
return (AuthenticateResponse) obj.response;
}
public RefreshResponse refresh(String authToken, UUID clientId) throws IOException, Throwable {
NetworkResponse obj = makeRequest("refresh", new RefreshRequest(authToken, clientId), RefreshResponse.class);
if (obj == null) {
return null;
} else {
obj.throwExceptionIfNeed(); // "Invalid username or password, status code: " + obj.statusCode);
return (RefreshResponse) obj.response;
}
}
public NetworkResponse validate(String authToken) throws Throwable {
return makeRequest("validate", new RefreshRequest(authToken, null), null);
}
public NetworkResponse invalidate(String authToken, UUID clientId) throws Throwable {
return makeRequest("invalidate", new RefreshRequest(authToken, clientId), null);
}

View File

@ -358,7 +358,7 @@ public class MinecraftDownloaderTask extends AsyncTask<String, String, Throwable
}
@Override
protected void onPostExecute(Throwable p1)
public void onPostExecute(Throwable p1)
{
mActivity.mPlayButton.setText("Play");
mActivity.mPlayButton.setEnabled(true);

View File

@ -82,57 +82,14 @@
app:layout_constraintTop_toTopOf="@+id/login_menu"
app:layout_constraintVertical_bias="0.088" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_online_password_hint"
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
app:layout_constraintStart_toStartOf="@+id/textInputLayout"
app:layout_constraintTop_toTopOf="@+id/textInputLayout" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@+id/login_menu"
app:layout_constraintEnd_toEndOf="@+id/login_edit_email"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/login_edit_email"
app:layout_constraintTop_toBottomOf="@+id/login_edit_email"
app:layout_constraintVertical_bias="0.08"
app:passwordToggleEnabled="true"
app:passwordToggleTint="@android:color/white">
<com.kdt.mcgui.MineEditText
android:id="@+id/login_edit_password"
android:layout_width="match_parent"
android:layout_height="30dp"
android:imeOptions="flagNoExtractUi"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
<CheckBox
android:id="@+id/login_switch_remember"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:text="@string/login_online_check_keeplogin"
app:layout_constraintStart_toStartOf="@+id/textInputLayout"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout" />
<CheckBox
android:id="@+id/login_switch_offline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:text="@string/login_offline_label"
app:layout_constraintStart_toStartOf="@+id/login_switch_remember"
app:layout_constraintTop_toBottomOf="@+id/login_switch_remember" />
app:layout_constraintStart_toStartOf="@+id/login_edit_email"
app:layout_constraintTop_toBottomOf="@+id/login_edit_email" />
<com.kdt.mcgui.MineButton
android:id="@+id/mineButton"
@ -145,8 +102,8 @@
android:textColor="@android:color/white"
app:layout_constraintEnd_toStartOf="@+id/guidelineLeft"
app:layout_constraintStart_toStartOf="@+id/login_switch_offline"
app:layout_constraintTop_toBottomOf="@+id/login_switch_offline" />
app:layout_constraintStart_toStartOf="@+id/login_switch_local"
app:layout_constraintTop_toBottomOf="@+id/login_switch_local" />
<ProgressBar
android:id="@+id/launcherAccProgress"
@ -174,7 +131,7 @@
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="@+id/mineButton"
app:layout_constraintEnd_toEndOf="@+id/textInputLayout"
app:layout_constraintEnd_toEndOf="@+id/login_edit_email"
app:layout_constraintStart_toStartOf="@+id/guidelineLeft"
app:layout_constraintTop_toTopOf="@+id/mineButton" />
@ -190,4 +147,4 @@
app:layout_constraintStart_toStartOf="@+id/mineButton"
app:layout_constraintTop_toBottomOf="@+id/mineButton" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,16 +10,12 @@
<!-- Languages list part -->
<!-- Login strings -->
<string name="login_online_username_hint">Email or username</string>
<string name="login_online_password_hint">Password</string>
<string name="login_online_username_hint">Local username</string>
<string name="login_online_check_keeplogin">Keep me logged in</string>
<string name="login_online_login_label">Login</string>
<string name="login_offline_label">Login as offline account</string>
<string name="login_error_invalid_username">Invalid username. Username must be in range of 3-16 characters, should only contains A-Z, a-z, 0-9 and underscore.</string>
<string name="login_error_exist_username">This username already exist</string>
<string name="login_error_offline_password">Offline account can\'t include password!</string>
<string name="login_microsoft">Microsoft login</string>
@ -58,7 +54,7 @@
<!-- MCLauncherActivity: Account status -->
<string name="mcl_account_connected">Connected</string>
<string name="mcl_account_offline">Offline</string>
<string name="mcl_account_local">Local</string>
<!-- MCLauncherActivity: Strings -->
<string name="mcl_version_msg">Ready to play Minecraft %s</string>
@ -66,6 +62,7 @@
<string name="mcl_launch_downloading">Downloading %s</string>
<string name="mcl_launch_downloading_progress">"Downloading %s (%.2f MB / %.2f MB)"</string>
<string name="mcl_launch_download_assets">Preparing to download resources</string>
<string name="mcl_launch_error_localmode">Minecraft can\'t be legally installed when logged in with a local account. Please switch to an online account to continue.</string>
<string name="mcl_options">Options</string>
<string name="mcl_option_modinstall">Launch a mod installer (Forge, LabyMod, Fabric, etc...)</string>