Skip to content

Commit 3afd235

Browse files
committed
feat: merge custom changes (boost, hidden-button, vibration, ci-fix)
Cherry-picked from 6b7e4bc onto upstream v12.9.8 base. Rollback upstream v12.10 (framegen) due to audio regression.
1 parent 4d0fe01 commit 3afd235

22 files changed

Lines changed: 2179 additions & 22 deletions
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Upstream Sync
2+
3+
on:
4+
schedule:
5+
- cron: '0 2 * * *' # every day 02:00 UTC
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
sync:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout master
16+
uses: actions/checkout@v4
17+
with:
18+
ref: master
19+
fetch-depth: 0
20+
token: ${{ secrets.PAT_TOKEN }}
21+
22+
- name: Get latest upstream release
23+
id: upstream
24+
run: |
25+
RESPONSE=$(curl -sfL "https://api.github.com/repos/qiin2333/moonlight-android/releases/latest")
26+
if [ -z "$RESPONSE" ]; then
27+
echo "::error::curl failed or returned empty response"
28+
exit 1
29+
fi
30+
VERSION=$(echo "$RESPONSE" | jq -r '.tag_name')
31+
if [ "$VERSION" = "null" ] || [ -z "$VERSION" ]; then
32+
echo "::error::tag_name not found in API response"
33+
echo "$RESPONSE" | jq . >&2
34+
exit 1
35+
fi
36+
echo "version=$VERSION" >> $GITHUB_OUTPUT
37+
echo "Upstream latest release: $VERSION"
38+
39+
- name: Check if already synced
40+
id: check
41+
run: |
42+
VERSION="${{ steps.upstream.outputs.version }}"
43+
if git ls-remote --tags origin "refs/tags/${VERSION}" | grep -q "${VERSION}"; then
44+
echo "already_synced=true" >> $GITHUB_OUTPUT
45+
echo "Tag ${VERSION} already exists in this repo, skipping."
46+
else
47+
echo "already_synced=false" >> $GITHUB_OUTPUT
48+
echo "New upstream version ${VERSION} detected."
49+
fi
50+
51+
- name: Add upstream remote and fetch
52+
if: steps.check.outputs.already_synced == 'false'
53+
run: |
54+
git remote add upstream https://github.com/qiin2333/moonlight-android.git
55+
git fetch upstream --tags
56+
57+
- name: Attempt merge
58+
if: steps.check.outputs.already_synced == 'false'
59+
id: merge
60+
run: |
61+
VERSION="${{ steps.upstream.outputs.version }}"
62+
git config user.name "todou"
63+
git config user.email "pslzj@outlook.com"
64+
65+
if git merge "${VERSION}" --no-edit --allow-unrelated-histories; then
66+
echo "success=true" >> $GITHUB_OUTPUT
67+
echo "Merge succeeded."
68+
else
69+
echo "success=false" >> $GITHUB_OUTPUT
70+
git merge --abort || true
71+
echo "Merge failed due to conflicts."
72+
fi
73+
74+
- name: Push merged master and create release tag
75+
if: steps.check.outputs.already_synced == 'false' && steps.merge.outputs.success == 'true'
76+
run: |
77+
VERSION="${{ steps.upstream.outputs.version }}"
78+
git push origin master
79+
git tag -f "$VERSION"
80+
git push origin "$VERSION"
81+
echo "Pushed tag $VERSION — android-ci.yml build triggered."
82+
83+
- name: Fail on merge conflict (GitHub will send email notification)
84+
if: steps.check.outputs.already_synced == 'false' && steps.merge.outputs.success == 'false'
85+
run: |
86+
echo "::error::Merge conflict detected with upstream ${{ steps.upstream.outputs.version }}."
87+
echo "::error::Please resolve manually: git merge ${{ steps.upstream.outputs.version }}"
88+
exit 1

app/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ android {
123123
buildTypes {
124124
debug {
125125
applicationIdSuffix ".vplus_debug"
126-
resValue "string", "app_label", "Moonlight V+"
127-
resValue "string", "app_label_root", "Moonlight V+"
126+
resValue "string", "app_label", "Moonlight T+"
127+
resValue "string", "app_label_root", "Moonlight T+"
128128

129129
minifyEnabled true
130130
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -161,8 +161,8 @@ android {
161161
//
162162
// TL;DR: Leave the following line alone!
163163
applicationIdSuffix ".qiin"
164-
resValue "string", "app_label", "Moonlight V+"
165-
resValue "string", "app_label_root", "Moonlight (Root)"
164+
resValue "string", "app_label", "Moonlight T+"
165+
resValue "string", "app_label_root", "Moonlight T+ (Root)"
166166

167167
minifyEnabled true
168168
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

app/src/main/java/com/limelight/binding/input/advance_setting/element/AnalogStick.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ private enum CLICK_STATE {
152152

153153
private ElementController.SendEventHandler middleValueSendHandler;
154154
private ElementController.SendEventHandler valueSendHandler;
155+
private StickSwipTrigger swipTriggerButton;
155156
private String middleValue;
156157
private String value;
157158
private int radius;
@@ -261,6 +262,7 @@ public AnalogStick(Map<String, Object> attributesMap,
261262
System.out.println("加载按摇杆时发生错误,已应用默认值: " + e.getMessage());
262263
}
263264
middleValueSendHandler = controller.getSendEventHandler(middleValue);
265+
swipTriggerButton = new StickSwipTrigger(attributesMap, controller);
264266
radius_complete = getPercent(radius, 100) - 2 * thick;
265267
radius_dead_zone = getPercent(radius, deadZoneRadius);
266268
radius_analog_stick = getPercent(radius, 20);
@@ -275,7 +277,7 @@ public void onMovement(float x, float y) {
275277

276278
@Override
277279
public void onClick() {
278-
elementController.buttonVibrator();
280+
swipTriggerButton.onStickPressed();
279281
}
280282

281283
@Override
@@ -358,6 +360,8 @@ protected void onElementDraw(Canvas canvas) {
358360
canvas.drawRect(rect, paintEdit);
359361

360362
}
363+
364+
swipTriggerButton.drawTriggerPreview(canvas, radius, radius, radius_complete);
361365
}
362366

363367
private void updatePosition(long eventTime) {
@@ -409,6 +413,7 @@ public boolean onElementTouchEvent(MotionEvent event) {
409413

410414
// get radius and angel of movement from center
411415
movement_radius = getMovementRadius(relative_x, relative_y);
416+
double rawMovementRadius = movement_radius;
412417
movement_angle = getAngle(relative_x, relative_y);
413418

414419
// pass touch event to parent if out of outer circle
@@ -455,8 +460,10 @@ public boolean onElementTouchEvent(MotionEvent event) {
455460
if (isPressed()) {
456461
// when is pressed calculate new positions (will trigger movement if necessary)
457462
updatePosition(event.getEventTime());
463+
swipTriggerButton.update(rawMovementRadius, radius_complete);
458464
} else {
459465
stick_state = AnalogStick.STICK_STATE.NO_MOVEMENT;
466+
swipTriggerButton.release();
460467
notifyOnRevoke();
461468

462469
// not longer pressed reset analog stick
@@ -479,9 +486,13 @@ protected SuperPageLayout getInfoPage() {
479486

480487
NumberSeekbar radiusNumberSeekbar = analogStickPage.findViewById(R.id.page_analog_stick_radius);
481488
TextView middleValueTextView = analogStickPage.findViewById(R.id.page_analog_stick_middle_value);
489+
TextView swipTriggerValueTextView = analogStickPage.findViewById(R.id.page_analog_stick_special_value);
490+
Switch stickPressVibrationSwitch = analogStickPage.findViewById(R.id.page_analog_stick_press_vibration);
491+
RadioGroup triggerModeGroup = analogStickPage.findViewById(R.id.page_analog_stick_trigger_mode);
482492
RadioGroup modeRadioGroup = analogStickPage.findViewById(R.id.page_analog_stick_value);
483493
Switch moveModeSwitch = analogStickPage.findViewById(R.id.page_analog_stick_move_mode);
484494
NumberSeekbar deadZoneRadiusNumberSeekbar = analogStickPage.findViewById(R.id.page_analog_stick_sense);
495+
NumberSeekbar swipTriggerRadiusSeekbar = analogStickPage.findViewById(R.id.page_analog_stick_special_trigger_radius);
485496
NumberSeekbar thickNumberSeekbar = analogStickPage.findViewById(R.id.page_analog_stick_thick);
486497
NumberSeekbar layerNumberSeekbar = analogStickPage.findViewById(R.id.page_analog_stick_layer);
487498
ElementEditText normalColorEditText = analogStickPage.findViewById(R.id.page_analog_stick_normal_color);
@@ -524,6 +535,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
524535
save();
525536
}
526537
});
538+
swipTriggerButton.bind(swipTriggerValueTextView, stickPressVibrationSwitch, triggerModeGroup, R.id.page_analog_stick_trigger_mode_hold, swipTriggerRadiusSeekbar, pageDeviceController, this::save, this::invalidate);
527539

528540
centralXNumberSeekbar.setProgressMin(centralXMin);
529541
centralXNumberSeekbar.setProgressMax(centralXMax);
@@ -660,6 +672,7 @@ public void onClick(View v) {
660672
contentValues.put(COLUMN_INT_ELEMENT_PRESSED_COLOR, pressedColor);
661673
contentValues.put(COLUMN_INT_ELEMENT_BACKGROUND_COLOR, backgroundColor);
662674
contentValues.put(COLUMN_INT_ELEMENT_MODE, moveMode);
675+
swipTriggerButton.putExtraAttributes(contentValues);
663676
elementController.addElement(contentValues);
664677
}
665678
});
@@ -693,6 +706,7 @@ public void save() {
693706
contentValues.put(COLUMN_INT_ELEMENT_PRESSED_COLOR, pressedColor);
694707
contentValues.put(COLUMN_INT_ELEMENT_BACKGROUND_COLOR, backgroundColor);
695708
contentValues.put(COLUMN_INT_ELEMENT_MODE, moveMode);
709+
swipTriggerButton.putExtraAttributes(contentValues);
696710
elementController.updateElement(elementId, contentValues);
697711

698712
}
@@ -774,4 +788,4 @@ public static ContentValues getInitialInfo() {
774788

775789
}
776790

777-
}
791+
}

app/src/main/java/com/limelight/binding/input/advance_setting/element/DigitalMovableButton.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public interface DigitalMovableButtonListener {
8686

8787
private int enableTouch = 0; // 0 = 按钮, 1 = 摇杆。
8888
private boolean isTrackpadMode = false;
89+
private boolean enableVibration = true; // 按下震动,默认启用
8990

9091
private int radius;
9192
private int sense;
@@ -193,6 +194,12 @@ public DigitalMovableButton(Map<String, Object> attributesMap,
193194
this.isTrackpadMode = (Boolean) trackpadValue;
194195
}
195196
}
197+
if (extraAttrs != null && extraAttrs.containsKey("enableVibration")) {
198+
Object vibrationValue = extraAttrs.get("enableVibration");
199+
if (vibrationValue instanceof Boolean) {
200+
this.enableVibration = (Boolean) vibrationValue;
201+
}
202+
}
196203
} catch (Exception e) {
197204
// 如果 JSON 格式错误,打印错误日志并使用默认值继续运行
198205
e.printStackTrace();
@@ -243,7 +250,7 @@ public void onRelease() {
243250
if (confirmedMove) return;
244251
confirmedDrag = true;
245252
listener.onClick();
246-
elementController.buttonVibrator();
253+
if (enableVibration) elementController.buttonVibrator();
247254
setPressed(true);
248255
invalidate();
249256
};
@@ -367,7 +374,7 @@ private boolean handleTrackpadTouchEvent(MotionEvent event) {
367374
if (confirmedDrag) {
368375
listener.onRelease();
369376
} else if (!confirmedMove) {
370-
elementController.buttonVibrator();
377+
if (enableVibration) elementController.buttonVibrator();
371378
listener.onClick();
372379
handler.postDelayed(listener::onRelease, 50);
373380
}
@@ -383,7 +390,7 @@ private boolean handleButtonTouchEvent(MotionEvent event) {
383390
int action = event.getActionMasked();
384391
switch (action) {
385392
case MotionEvent.ACTION_DOWN: {
386-
elementController.buttonVibrator();
393+
if (enableVibration) elementController.buttonVibrator();
387394
lastX = event.getX();
388395
lastY = event.getY();
389396
setPressed(true);
@@ -414,7 +421,7 @@ private boolean handleJoystickTouchEvent(MotionEvent event) {
414421
int action = event.getActionMasked();
415422
switch (action) {
416423
case MotionEvent.ACTION_DOWN:
417-
elementController.buttonVibrator();
424+
if (enableVibration) elementController.buttonVibrator();
418425
setPressed(true);
419426
onClickCallback();
420427
invalidate();
@@ -478,6 +485,7 @@ public void save() {
478485
// --- 创建并保存 extra_attributes 的 JSON 字符串 ---
479486
Map<String, Object> extraAttrs = new HashMap<>();
480487
extraAttrs.put("isTrackpadMode", isTrackpadMode);
488+
extraAttrs.put("enableVibration", enableVibration);
481489

482490
Gson gson = new Gson();
483491
String json = gson.toJson(extraAttrs);
@@ -508,6 +516,7 @@ protected SuperPageLayout getInfoPage() {
508516
View joystickModeContainer = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_joystick_mode_container);
509517
Switch trackpadModeSwitch = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_trackpad_mode);
510518
View trackpadModeContainer = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_trackpad_mode_container);
519+
Switch vibrationSwitch = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_vibration);
511520
TextView valueTextView = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_value);
512521

513522
Runnable updateValueViewState = () -> {
@@ -581,6 +590,13 @@ protected SuperPageLayout getInfoPage() {
581590
save();
582591
});
583592

593+
// --- 震动开关 ---
594+
vibrationSwitch.setChecked(enableVibration);
595+
vibrationSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
596+
enableVibration = isChecked;
597+
save();
598+
});
599+
584600
NumberSeekbar widthNumberSeekbar = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_width);
585601
NumberSeekbar heightNumberSeekbar = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_height);
586602
NumberSeekbar radiusNumberSeekbar = digitalMovableButtonPage.findViewById(R.id.page_digital_movable_button_radius);
@@ -797,7 +813,7 @@ public void onStopTrackingTouch(SeekBar seekBar) {
797813
contentValues.put(COLUMN_STRING_ELEMENT_TEXT, text);
798814
contentValues.put(COLUMN_STRING_ELEMENT_VALUE, value);
799815
contentValues.put(COLUMN_INT_ELEMENT_MODE, enableTouch);
800-
contentValues.put(COLUMN_STRING_EXTRA_ATTRIBUTES, new Gson().toJson(new HashMap<String, Object>() {{ put("isTrackpadMode", isTrackpadMode); }}));
816+
contentValues.put(COLUMN_STRING_EXTRA_ATTRIBUTES, new Gson().toJson(new HashMap<String, Object>() {{ put("isTrackpadMode", isTrackpadMode); put("enableVibration", enableVibration); }}));
801817
contentValues.put(COLUMN_INT_ELEMENT_SENSE, sense);
802818
contentValues.put(COLUMN_INT_ELEMENT_WIDTH, getElementWidth());
803819
contentValues.put(COLUMN_INT_ELEMENT_HEIGHT, getElementHeight());

0 commit comments

Comments
 (0)