Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ public static final class ContentCounts {
public final int worlds;
public final int resourcePacks;
public final int skinPacks;
ContentCounts(int worlds, int resourcePacks, int skinPacks) {
public final int behaviorPacks;
ContentCounts(int worlds, int resourcePacks, int skinPacks, int behaviorPacks) {
this.worlds = worlds;
this.resourcePacks = resourcePacks;
this.skinPacks = skinPacks;
this.behaviorPacks = behaviorPacks;
}
}

Expand Down Expand Up @@ -106,7 +108,8 @@ public synchronized void configureStorageDirectories(File worldsDir, File resour
serversLiveData.setValue(java.util.Collections.emptyList());
String key = countKey(selected);
countsLiveData.setValue(new ContentCounts(countPreferences.getInt(key + "w", 0),
countPreferences.getInt(key + "r", 0), countPreferences.getInt(key + "s", 0)));
countPreferences.getInt(key + "r", 0), countPreferences.getInt(key + "s", 0),
countPreferences.getInt(key + "b", 0)));
};
if (android.os.Looper.myLooper() == android.os.Looper.getMainLooper()) clear.run();
else mainHandler.post(clear);
Expand Down Expand Up @@ -163,10 +166,12 @@ public void refreshCounts() {
File[] root = locations;
ContentCounts counts = new ContentCounts(worldManager.countWorlds(root[0]),
resourcePackManager.countPacks(root[1], ResourcePackItem.PackType.RESOURCE_PACK),
resourcePackManager.countPacks(root[3], ResourcePackItem.PackType.SKIN_PACK));
resourcePackManager.countPacks(root[3], ResourcePackItem.PackType.SKIN_PACK),
resourcePackManager.countPacks(root[2], ResourcePackItem.PackType.BEHAVIOR_PACK));
String key = countKey(root);
countPreferences.edit().putInt(key + "w", counts.worlds)
.putInt(key + "r", counts.resourcePacks).putInt(key + "s", counts.skinPacks).apply();
.putInt(key + "r", counts.resourcePacks).putInt(key + "s", counts.skinPacks)
.putInt(key + "b", counts.behaviorPacks).apply();
return counts;
}, countsLiveData::setValue);
}
Expand All @@ -183,6 +188,7 @@ public void refreshResourcePacks() {

public void refreshBehaviorPacks() {
requestRefresh(2, refreshExecutor, resourcePackManager::getBehaviorPacks, behaviorPacksLiveData::setValue);
refreshCounts();
}

public void refreshSkinPacks() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@

import org.levimc.launcher.BuildConfig;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import org.levimc.launcher.core.curseforge.models.ContentSearchResponse;
import org.levimc.launcher.core.curseforge.models.ModFilesResponse;
import org.levimc.launcher.core.curseforge.models.StringResponse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
Expand Down Expand Up @@ -169,4 +177,79 @@ public void onResponse(Call call, Response response) {
}
});
}

/** The game's versions (newest first), for the search's game version filter. */
public void getGameVersions(CurseForgeCallback<List<String>> callback) {
Request request = new Request.Builder()
.url(BASE_URL + "/v2/games/" + GAME_ID_MINECRAFT + "/versions")
.addHeader("x-api-key", API_KEY)
.addHeader("Accept", "application/json")
.build();

client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
callback.onError(e);
}

@Override
public void onResponse(Call call, Response response) {
if (!response.isSuccessful()) {
callback.onError(new IOException("Unexpected code " + response));
return;
}
try {
String json = response.body().string();
callback.onSuccess(parseGameVersions(json));
} catch (Exception e) {
callback.onError(e);
}
}
});
}

// v2 shape: {"data":[{"type":N,"versions":[{"id":..,"slug":..,"name":"1.21.0"}, ...]}, ...]}
static List<String> parseGameVersions(String json) {
Set<String> names = new LinkedHashSet<>();
JsonObject root = JsonParser.parseString(json).getAsJsonObject();
JsonArray data = root.has("data") && root.get("data").isJsonArray() ? root.getAsJsonArray("data") : new JsonArray();
for (JsonElement group : data) {
if (!group.isJsonObject() || !group.getAsJsonObject().has("versions")) continue;
JsonElement versions = group.getAsJsonObject().get("versions");
if (!versions.isJsonArray()) continue;
for (JsonElement v : versions.getAsJsonArray()) {
String name = null;
if (v.isJsonPrimitive()) {
name = v.getAsString();
} else if (v.isJsonObject() && v.getAsJsonObject().has("name")) {
name = v.getAsJsonObject().get("name").getAsString();
}
if (name != null && !name.trim().isEmpty() && Character.isDigit(name.trim().charAt(0))) {
names.add(name.trim());
}
}
}
List<String> out = new ArrayList<>(names);
out.sort((a, b) -> compareVersions(b, a));
return out;
}

private static int compareVersions(String a, String b) {
String[] pa = a.split("[^0-9]+");
String[] pb = b.split("[^0-9]+");
for (int i = 0; i < Math.max(pa.length, pb.length); i++) {
long x = i < pa.length && !pa[i].isEmpty() ? parseLong(pa[i]) : 0;
long y = i < pb.length && !pb[i].isEmpty() ? parseLong(pb[i]) : 0;
if (x != y) return Long.compare(x, y);
}
return a.compareTo(b);
}

private static long parseLong(String s) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
return 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class MinecraftLoadingActivity : BaseActivity(), MinecraftRuntimePreparer.Progre

override fun shouldSkipNavBar(): Boolean = true

override fun avoidsCutoutWithoutShell(): Boolean = false

override fun onResume() {
super.onResume()
hideSystemUi()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ class LauncherRestartActivity : BaseActivity() {

override fun shouldSkipNavBar(): Boolean = true

override fun avoidsCutoutWithoutShell(): Boolean = false

override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
registerCloseReceiver()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package org.levimc.launcher.ui.activities;

import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.material.button.MaterialButton;

import org.levimc.launcher.R;
import org.levimc.launcher.ui.animation.DynamicAnim;
import org.levimc.launcher.ui.widget.UiKit;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -27,6 +33,8 @@ public class AboutActivity extends BaseActivity {
private static final String URL_ISSUES = "https://github.com/LiteLDev/LeviLauncher-Android/issues";
private static final String MAINTAINER_AVATAR_URL = "https://yt3.googleusercontent.com/ft4khqXZ_fQn-DbSLg91kQy3_JUQ_73rbg18nOcmMtunX5bq25jzrThWQAk9YsFkTKFesUL7sg8=s160-c-k-c0x00ffffff-no-rj";
private static final String URL_YOUTUBE = "https://www.youtube.com/c/mrpokeg";
private static final String URL_LICENSE = "https://github.com/LiteLDev/LeviLauncher-Android/blob/HEAD/LICENSE";
private static final String URL_RHYMC = "https://www.rhymc.com/";

private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final OkHttpClient client = new OkHttpClient();
Expand All @@ -47,28 +55,37 @@ protected void onCreate(Bundle savedInstanceState) {
private void styleBadges() {
TextView authorBadge = findViewById(R.id.author_badge);
TextView maintainerBadge = findViewById(R.id.maintainer_badge);
if (authorBadge != null) UiKit.tintSoftAccent(authorBadge, 7);
if (maintainerBadge != null) UiKit.tintSoftAccent(maintainerBadge, 7);

tintChip(R.id.about_authors_chip, R.id.about_authors_icon, UiKit.accent(this));
tintChip(R.id.about_thanks_chip, R.id.about_thanks_icon, UiKit.AMBER);
tintChip(R.id.about_rhymc_chip, R.id.about_rhymc_icon, UiKit.BLUE);
tintChip(R.id.about_sponsors_chip, R.id.about_sponsors_icon, UiKit.ROSE);
tintChip(R.id.about_source_chip, R.id.about_source_icon, UiKit.BLUE);

// Brand icons keep their own colors (the YouTube logo has a white play mark).
clearIconTint(R.id.btn_afdian);
clearIconTint(R.id.btn_patreon);
clearIconTint(R.id.btn_youtube_maintainer);

MaterialButton starFork = findViewById(R.id.btn_star_fork);
if (starFork != null) UiKit.applyPrimary(starFork);
}

private void tintChip(int chipId, int iconId, int color) {
View chip = findViewById(chipId);
ImageView icon = findViewById(iconId);
if (chip != null) UiKit.tintIconChip(chip, icon, color);
}

org.levimc.launcher.util.PersonalizationManager pm = new org.levimc.launcher.util.PersonalizationManager(this);
int accent = pm.getAccentColor();
if (accent != 0) {
android.graphics.drawable.GradientDrawable gd = new android.graphics.drawable.GradientDrawable();
gd.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
gd.setColor(accent);
gd.setCornerRadius(4 * getResources().getDisplayMetrics().density);
if (authorBadge != null) {
authorBadge.setBackground(gd);
authorBadge.setTextColor(android.graphics.Color.WHITE);
}
if (maintainerBadge != null) {
maintainerBadge.setBackground(gd);
maintainerBadge.setTextColor(android.graphics.Color.WHITE);
}
}
private void clearIconTint(int buttonId) {
MaterialButton button = findViewById(buttonId);
if (button != null) button.setIconTint((ColorStateList) null);
}

private void setupNavBar() {
setActiveNavTab(R.id.nav_tab_about);
findViewById(R.id.nav_tab_about).setOnClickListener(v -> {});
}

private void loadAvatars() {
Expand Down Expand Up @@ -110,6 +127,8 @@ private void setupLinks() {
setupLinkButton(R.id.btn_issues, URL_ISSUES);
setupLinkButton(R.id.btn_star_fork, URL_REPO);
setupLinkButton(R.id.btn_youtube_maintainer, URL_YOUTUBE);
setupLinkButton(R.id.btn_license, URL_LICENSE);
setupLinkButton(R.id.btn_rhymc_website, URL_RHYMC);
}

private void setupLinkButton(int viewId, String url) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
Expand All @@ -16,12 +15,15 @@
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.google.android.material.button.MaterialButton;

import org.levimc.launcher.R;
import org.levimc.launcher.core.auth.MsftAccountStore;
import org.levimc.launcher.core.auth.MsftAuthManager;
import org.levimc.launcher.ui.adapter.AccountsAdapter;
import org.levimc.launcher.ui.animation.DynamicAnim;
import org.levimc.launcher.ui.dialogs.LoadingDialog;
import org.levimc.launcher.ui.widget.UiKit;
import org.levimc.launcher.util.AccountTextUtils;
import org.levimc.launcher.util.PersonalizationManager;

Expand Down Expand Up @@ -55,6 +57,7 @@ public class AccountsActivity extends BaseActivity {
private ActivityResultLauncher<Intent> loginLauncher;
private ExecutorService executor = Executors.newSingleThreadExecutor();
private LoadingDialog loadingDialog;
private boolean shownEnabled;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -89,6 +92,12 @@ protected void onCreate(Bundle savedInstanceState) {
if (bottomAddButton != null) bottomAddButton.setOnClickListener(addAction);
if (bottomAddButton != null) DynamicAnim.applyPressScale(bottomAddButton);

View goSettings = findViewById(R.id.accounts_go_settings);
if (goSettings != null) {
goSettings.setOnClickListener(v -> startActivity(new Intent(this, SettingsActivity.class)));
DynamicAnim.applyPressScale(goSettings);
}

adapter.setOnAccountActionListener(new AccountsAdapter.OnAccountActionListener() {
@Override
public void onSetActive(MsftAccountStore.MsftAccount account) {
Expand Down Expand Up @@ -147,10 +156,10 @@ private void applyAccountAccent() {
if (avatarProgress != null) {
avatarProgress.setIndeterminateTintList(ColorStateList.valueOf(accent));
}
if (bottomAddButton != null) {
bottomAddButton.setBackgroundTintList(ColorStateList.valueOf(accent));
bottomAddButton.setTextColor(Color.WHITE);
if (bottomAddButton instanceof MaterialButton) {
UiKit.applyPrimary((MaterialButton) bottomAddButton);
}
if (privilegesText != null) UiKit.tintSoftAccent(privilegesText, 7);
}

private MsftAccountStore.MsftAccount getActiveAccount() {
Expand All @@ -163,18 +172,21 @@ private MsftAccountStore.MsftAccount getActiveAccount() {

private void refreshUI() {
boolean enabled = org.levimc.launcher.settings.FeatureSettings.getInstance().isLauncherManagedMcLoginEnabled();
shownEnabled = enabled;
View mainContent = findViewById(R.id.main_content_container);
View disabledState = findViewById(R.id.disabled_state_container);

if (mainContent != null) mainContent.setVisibility(enabled ? View.VISIBLE : View.GONE);
if (disabledState != null) disabledState.setVisibility(enabled ? View.GONE : View.VISIBLE);
if (bottomAddButton != null) bottomAddButton.setVisibility(enabled ? View.VISIBLE : View.GONE);

if (!enabled) return;

MsftAccountStore.MsftAccount active = getActiveAccount();
if (active == null) {
gamertagText.setText(getString(R.string.not_signed_in));
privilegesText.setText("");
privilegesText.setVisibility(View.GONE);
xuidText.setText("");
if (xboxAvatar != null) {
xboxAvatar.setImageDrawable(null);
Expand All @@ -186,6 +198,7 @@ private void refreshUI() {
String displayName = AccountTextUtils.displayNameOrNotSigned(this, active);
gamertagText.setText(displayName);
privilegesText.setText(getString(R.string.accounts_active_privilege));
privilegesText.setVisibility(View.VISIBLE);
xuidText.setText(active.xuid != null ? active.xuid : "");
loadXboxAvatar(active);
if (rightCardContainer != null) rightCardContainer.setVisibility(View.VISIBLE);
Expand All @@ -200,6 +213,15 @@ private void refreshUI() {
refreshNavAccountUI();
}

@Override
protected void onResume() {
super.onResume();
// The feature switch may have been changed in Settings ("Go to Settings").
if (isFinishing() || gamertagText == null) return;
boolean enabled = org.levimc.launcher.settings.FeatureSettings.getInstance().isLauncherManagedMcLoginEnabled();
if (enabled != shownEnabled) refreshUI();
}

@Override
protected void onNavAccountChanged() {
refreshUI();
Expand Down
Loading
Loading