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
12 changes: 10 additions & 2 deletions app/src/main/java/cn/classfun/droidvm/lib/size/SizeUtils.java

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

拆分为独立提交

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ public static SizeNumber findUnit(
for (int i = units.length - 1; i >= 0; i--)
if (units[i].fitsExactly(bytes))
return units[i].calcPair(bytes);
return SizeUnit.B.calcPair(bytes);
// Nothing fits exactly: fall back to the smallest *allowed* unit so a
// restricted list (e.g. a GiB-only picker) never yields a unit outside
// it. For the full list units[0] is B - identical to the old fallback.
return units.length > 0
? units[0].calcPair(new BigDecimal(bytes))
: SizeUnit.B.calcPair(bytes);
}

@NonNull
Expand All @@ -31,7 +36,10 @@ public static SizeNumber findFloatUnit(
for (int i = units.length - 1; i >= 0; i--)
if (units[i].isAtLeast(bytes))
return units[i].calcPair(bytes);
return SizeUnit.B.calcPair(bytes);
// See findUnit: keep the fallback inside the allowed list.
return units.length > 0
? units[0].calcPair(bytes)
: SizeUnit.B.calcPair(bytes);
}

@NonNull
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/cn/classfun/droidvm/lib/utils/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ public static String pathJoin(@NonNull File base, @NonNull String... children) {
return pathJoin(base.getAbsolutePath(), children);
}

/** Join the non-empty parts with {@code sep}; empty parts are skipped. */
@NonNull
public static String joinNonEmpty(@NonNull String sep, @NonNull String... parts) {
var sb = new StringBuilder();
for (var p : parts) {
if (p.isEmpty()) continue;
if (sb.length() > 0) sb.append(sep);
sb.append(p);
}
return sb.toString();
}

@NonNull
public static String fmt(String fmt, Object... args) {
return new Formatter(Locale.ROOT).format(fmt, args).toString();
Expand Down
528 changes: 500 additions & 28 deletions app/src/main/java/cn/classfun/droidvm/ui/hugepage/HugePageActivity.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,32 @@
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;

import cn.classfun.droidvm.R;

/**
* Deterministic per-process colors for the hugepage usage bar and list icons.
* The same pid always maps to the same hue (stable across refreshes); in dark
* mode colors are light, in light mode they are dark, so they read against the
* surface either way.
* In dark mode colors are light, in light mode they are dark, so they read
* against the surface either way.
*
* <p>Hues are assigned by golden-angle steps over each pid's <b>rank among the
* pids currently shown</b> (ascending pid order), not over the raw pid. Keying
* the angle by pid could land two live VMs almost on the same hue (any pid
* difference near a multiple of 360/137.508 - e.g. 34 - wraps to within a few
* degrees), whereas consecutive ranks are always 137.5&deg; apart, so no two of
* up to ~7 VMs come closer than ~32&deg; (&ge;52&deg; for four or fewer). Ranks
* are stable while the same VMs run - new pids are usually larger and append at
* the end - and both hugepage screens derive the map from the same owner list,
* so a VM keeps one color everywhere until the set of VMs itself changes.
*/
final class HugePageColor {
/** Golden angle in degrees: consecutive ranks land maximally apart. */
private static final float GOLDEN_ANGLE = 137.508f;

private HugePageColor() {
}

Expand All @@ -25,9 +42,22 @@ static boolean isDark(Context ctx) {
return mode == Configuration.UI_MODE_NIGHT_YES;
}

/** Golden-angle hue spread keyed by pid, theme-aware saturation/value. */
static int forPid(int pid, boolean dark) {
float hue = ((pid * 137.508f) % 360f + 360f) % 360f;
/**
* Assign a color to every pid in {@code pids} (duplicates collapse), keyed
* by ascending-pid rank. Pass the <b>full</b> pid list of the screen (not a
* filtered subset) so both hugepage screens agree on the ranks.
*/
@NonNull
static Map<Integer, Integer> forPids(@NonNull Collection<Integer> pids, boolean dark) {
var map = new LinkedHashMap<Integer, Integer>();
int rank = 0;
for (var pid : new TreeSet<>(pids)) map.put(pid, forRank(rank++, dark));
return map;
}

/** Golden-angle hue for one rank, theme-aware saturation/value. */
static int forRank(int rank, boolean dark) {
float hue = ((rank * GOLDEN_ANGLE) % 360f + 360f) % 360f;
float[] hsv = {hue, dark ? 0.50f : 0.72f, dark ? 0.90f : 0.55f};
return Color.HSVToColor(hsv);
}
Expand All @@ -36,4 +66,27 @@ static int forPid(int pid, boolean dark) {
static int pending(@NonNull Context ctx) {
return ContextCompat.getColor(ctx, R.color.hugepage_pending);
}

/** Reservoir portion currently occupied by other apps' allocations. */
static int cmaUsed(@NonNull Context ctx) {
return ContextCompat.getColor(ctx, R.color.hugepage_cma_used);
}

/** Reservoir portion still free in buddy. */
static int cmaFree(@NonNull Context ctx) {
return ContextCompat.getColor(ctx, R.color.hugepage_cma_free);
}

/** Available-pool portion not flippable to CMA as whole pageblocks. */
static int availNonCma(@NonNull Context ctx) {
return ContextCompat.getColor(ctx, R.color.hugepage_avail_non_cma);
}

/**
* Opaque gray for the synthetic "available" list row's icon - the bar's
* translucent track color is too faint as an icon tint.
*/
static int availIcon(boolean dark) {
return dark ? 0xFFB5B5B5 : 0xFF8A8A8A;
}
}
Loading
Loading