Skip to content

Commit

Permalink
ANDROID: Add a LED widget and use it to indicate IO activity in SAF
Browse files Browse the repository at this point in the history
This will notify the user of IOs by blinking the LED. This indicates
that ScummVM is busy and not hung.
  • Loading branch information
lephilousophe committed Jan 5, 2025
1 parent 41e49d8 commit 50658e6
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 0 deletions.
168 changes: 168 additions & 0 deletions backends/platform/android/org/scummvm/scummvm/LedView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.scummvm.scummvm;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

public class LedView extends View {
public static final int DEFAULT_LED_COLOR = 0xffff0000;
private static final int BLINK_TIME = 30; // ms

private boolean _state;
private Runnable _blink;
private Paint _painter;
private int _radius;
private int _centerX;
private int _centerY;

public LedView(Context context) {
this(context, true, DEFAULT_LED_COLOR);
}

public LedView(Context context, boolean state) {
this(context, state, DEFAULT_LED_COLOR);
}

public LedView(Context context, boolean state, int color) {
super(context);
_state = state;
init(color);
}

public LedView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public LedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}

@RequiresApi(android.os.Build.VERSION_CODES.LOLLIPOP)
public LedView(
Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}

private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.LedView,
defStyleAttr, defStyleRes);

try {
_state = a.getBoolean(R.styleable.LedView_state, true);
int color = a.getColor(R.styleable.LedView_color, DEFAULT_LED_COLOR);
init(color);
} finally {
a.recycle();
}
}

private void init(int color) {
_painter = new Paint();
_painter.setStyle(Paint.Style.FILL);
if (isInEditMode()) {
_painter.setStrokeWidth(2);
_painter.setStyle(_state ? Paint.Style.FILL : Paint.Style.STROKE);
}
_painter.setColor(color);
_painter.setAntiAlias(true);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int w = resolveSizeAndState(minw, widthMeasureSpec, 0);

int minh = MeasureSpec.getSize(w) - getPaddingLeft() - getPaddingRight() +
getPaddingBottom() + getPaddingTop();
int h = resolveSizeAndState(minh, heightMeasureSpec, 0);

setMeasuredDimension(w, h);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);

int xpad = (getPaddingLeft() + getPaddingRight());
int ypad = (getPaddingTop() + getPaddingBottom());

int ww = w - xpad;
int hh = h - ypad;

_radius = Math.min(ww, hh) / 2 - 2;
_centerX = w / 2;
_centerY = h / 2;
}

@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);

if (!_state && !isInEditMode()) {
return;
}

canvas.drawCircle(_centerX, _centerY, _radius, _painter);
}

public void on() {
setState(true);
}

public void off() {
setState(false);
}

public void setState(boolean state) {
if (_blink != null) {
removeCallbacks(_blink);
_blink = null;
}

if (_state == state) {
return;
}
_state = state;
invalidate();
}

public void blinkOnce() {
if (_blink != null) {
return;
}

boolean oldState = _state;
_state = !oldState;
invalidate();

_blink = new Runnable() {
private boolean _ran;

@Override
public void run() {
if (_ran) {
_blink = null;
return;
}

_ran = true;
_state = oldState;
invalidate();

postDelayed(this, BLINK_TIME);
}
};
postDelayed(_blink, BLINK_TIME);
}
}
22 changes: 22 additions & 0 deletions backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
private GridLayout _buttonLayout = null;
private ImageView _toggleTouchModeKeyboardBtnIcon = null;
private ImageView _openMenuBtnIcon = null;
private LedView _ioLed = null;
private int _layoutOrientation;

public View _screenKeyboard = null;
Expand Down Expand Up @@ -531,6 +532,9 @@ private void layoutButtonLayout(int orientation, boolean force) {
params = (GridLayout.LayoutParams)_toggleTouchModeKeyboardBtnIcon.getLayoutParams();
params.rowSpec = GridLayout.spec(1);
params.columnSpec = GridLayout.spec(1);
params = (GridLayout.LayoutParams)_ioLed.getLayoutParams();
params.rowSpec = GridLayout.spec(0, 2, GridLayout.TOP);
params.columnSpec = GridLayout.spec(0, GridLayout.RIGHT);
} else {
GridLayout.LayoutParams params;
params = (GridLayout.LayoutParams)_openMenuBtnIcon.getLayoutParams();
Expand All @@ -539,6 +543,9 @@ private void layoutButtonLayout(int orientation, boolean force) {
params = (GridLayout.LayoutParams)_toggleTouchModeKeyboardBtnIcon.getLayoutParams();
params.rowSpec = GridLayout.spec(0);
params.columnSpec = GridLayout.spec(0);
params = (GridLayout.LayoutParams)_ioLed.getLayoutParams();
params.rowSpec = GridLayout.spec(1, GridLayout.TOP);
params.columnSpec = GridLayout.spec(0, 2, GridLayout.RIGHT);
}
_buttonLayout.requestLayout();
}
Expand Down Expand Up @@ -932,6 +939,7 @@ public void onCreate(Bundle savedInstanceState) {
_buttonLayout = findViewById(R.id.button_layout);
_openMenuBtnIcon = findViewById(R.id.open_menu_button);
_toggleTouchModeKeyboardBtnIcon = findViewById(R.id.toggle_touch_button);
_ioLed = findViewById(R.id.io_led);

// Hide by default all buttons, they will be shown when native code will start
showToggleOnScreenBtnIcons(0);
Expand Down Expand Up @@ -1043,6 +1051,18 @@ public void handle(int exitResult) {
_main_surface.setOnHoverListener(_mouseHelper);
}

SAFFSTree.setIOBusyListener(new SAFFSTree.IOBusyListener() {
@Override
public void onIOBusy(float ratio) {
runOnUiThread(new Runnable() {
@Override
public void run() {
_ioLed.blinkOnce();
}
});
}
});

_scummvm_thread = new Thread(_scummvm, "ScummVM");
_scummvm_thread.start();
}
Expand Down Expand Up @@ -1159,6 +1179,8 @@ public void onDestroy() {

super.onDestroy();

SAFFSTree.setIOBusyListener(null);

if (isScreenKeyboardShown()) {
hideScreenKeyboard();
}
Expand Down
10 changes: 10 additions & 0 deletions dists/android/res/layout/scummvm_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@
android:src="@drawable/ic_action_menu"
android:visibility="gone"
tools:visibility="visible" />

<org.scummvm.scummvm.LedView
android:id="@+id/io_led"
android:layout_width="5dp"
android:layout_height="wrap_content"
android:layout_row="1"
android:layout_column="0"
android:layout_columnSpan="2"
android:layout_gravity="right|top"
app:state="false" />
</GridLayout>

</FrameLayout>
6 changes: 6 additions & 0 deletions dists/android/res/values/attrs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<resources>
<declare-styleable name="LedView">
<attr name="color" format="reference|color" />
<attr name="state" format="boolean" />
</declare-styleable>
</resources>

0 comments on commit 50658e6

Please sign in to comment.