[controller] rediseño y actualización en interacción de los botones atado a eventos
This commit is contained in:
parent
5eac64be96
commit
af7655b572
|
|
@ -112,12 +112,16 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
|
||||
// Cassette Deck UI
|
||||
private LinearLayout deckContainer;
|
||||
private Button btnServerControl;
|
||||
private ProgressButton btnServerControl;
|
||||
private ObjectAnimator fusionAnimator;
|
||||
private android.animation.ObjectAnimator exploreAnimator;
|
||||
private boolean isServerAlive = false;
|
||||
private boolean isNegotiating = false;
|
||||
private boolean isProxyDegraded = false;
|
||||
private Boolean targetServerState = null;
|
||||
private String serverTransitionText = "";
|
||||
private final Handler timeoutHandler = new Handler(android.os.Looper.getMainLooper());
|
||||
private Runnable timeoutRunnable;
|
||||
private String currentTargetUrl = null;
|
||||
private long pulseStartTime = 0;
|
||||
|
||||
|
|
@ -271,14 +275,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
// Listeners
|
||||
watchdogControl.setOnClickListener(v -> {
|
||||
boolean willBeEnabled = !prefs.getWatchdogEnable();
|
||||
if (willBeEnabled) {
|
||||
BiometricHelper.prompt(MainActivity.this,
|
||||
getString(R.string.unlock_watchdog_title),
|
||||
getString(R.string.unlock_watchdog_subtitle),
|
||||
() -> setWatchdogState(true));
|
||||
} else {
|
||||
setWatchdogState(false);
|
||||
}
|
||||
setWatchdogState(willBeEnabled);
|
||||
});
|
||||
btnClearLog.setOnClickListener(this);
|
||||
btnCopyLog.setOnClickListener(this);
|
||||
|
|
@ -294,30 +291,42 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
button_save.setOnClickListener(this);
|
||||
|
||||
btnServerControl.setOnClickListener(v -> {
|
||||
// We're simplifying the action for now to test
|
||||
if (btnServerControl.getText().toString().contains("Launch")) {
|
||||
btnServerControl.setText("Starting...");
|
||||
btnServerControl.setAlpha(0.7f); // Efecto visual de "Cargando"
|
||||
// Ignore clicks if we are already waiting for a state change
|
||||
if (targetServerState != null) return;
|
||||
|
||||
// Freeze the transition text and define the TARGET state
|
||||
serverTransitionText = !isServerAlive ? "Booting..." : "Shutting down...";
|
||||
targetServerState = !isServerAlive;
|
||||
|
||||
// Lock the UI and start infinite animation
|
||||
updateUIColorsAndVisibility();
|
||||
btnServerControl.startProgress();
|
||||
|
||||
// Set a hard timeout (45 seconds) as a safety net
|
||||
timeoutRunnable = () -> {
|
||||
if (targetServerState != null) {
|
||||
targetServerState = null; // Abort transition
|
||||
btnServerControl.stopProgress();
|
||||
updateUIColorsAndVisibility();
|
||||
addToLog("Warning: Server state transition timed out.");
|
||||
}
|
||||
};
|
||||
timeoutHandler.postDelayed(timeoutRunnable, 45000);
|
||||
|
||||
// Execute the corresponding script command
|
||||
if (!isServerAlive) {
|
||||
startTermuxEnvironmentVisible("--start");
|
||||
} else {
|
||||
// TODO: We'll add the VPN biometric validation here later
|
||||
btnServerControl.setText("Stopping...");
|
||||
btnServerControl.setAlpha(0.7f);
|
||||
startTermuxEnvironmentVisible("--stop");
|
||||
|
||||
// Automatically turn off the Watchdog if we take down the server.
|
||||
if (prefs.getWatchdogEnable()) setWatchdogState(false);
|
||||
// Turn off Watchdog gracefully when stopping the server manually
|
||||
if (prefs.getWatchdogEnable()) {
|
||||
setWatchdogState(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Logic to open the WebView (PortalActivity)
|
||||
// button_browse_content.setOnClickListener(v -> {
|
||||
// Intent intent = new Intent(MainActivity.this, PortalActivity.class);
|
||||
// // We tell the Portal exactly where to go
|
||||
// String urlToLoad = prefs.getEnable() ? "http://box/home" : "http://localhost:8085/home";
|
||||
// intent.putExtra("TARGET_URL", urlToLoad);
|
||||
// startActivity(intent);
|
||||
// });
|
||||
button_browse_content.setOnClickListener(v -> {
|
||||
if (currentTargetUrl != null) {
|
||||
Intent intent = new Intent(MainActivity.this, PortalActivity.class);
|
||||
|
|
@ -442,7 +451,13 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
// We evaluate the results
|
||||
isNegotiating = false;
|
||||
isServerAlive = boxAlive || localAlive;
|
||||
isProxyDegraded = !boxAlive && localAlive; // Tunnel on, but proxy dead
|
||||
|
||||
// If VPN is ON but box/proxy is dead, the tunnel is degraded (Orange).
|
||||
if (prefs.getEnable()) {
|
||||
isProxyDegraded = !boxAlive;
|
||||
} else {
|
||||
isProxyDegraded = false;
|
||||
}
|
||||
|
||||
if (boxAlive) {
|
||||
currentTargetUrl = "http://box/home";
|
||||
|
|
@ -690,7 +705,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
}
|
||||
|
||||
updateUI();
|
||||
updateUIColorsAndVisibility(isServerAlive);
|
||||
updateUIColorsAndVisibility();
|
||||
}
|
||||
|
||||
private void handleControlClick() {
|
||||
|
|
@ -795,13 +810,20 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
if (vpnOn) {
|
||||
// The passive radar must also use the proxy to test the tunnel.
|
||||
boxAlive = pingUrl("http://box/home", true);
|
||||
isProxyDegraded = !boxAlive && localAlive;
|
||||
isProxyDegraded = !boxAlive;
|
||||
} else {
|
||||
isProxyDegraded = false;
|
||||
}
|
||||
|
||||
isServerAlive = localAlive || boxAlive;
|
||||
|
||||
// STATE MACHINE: Has the target state been reached?
|
||||
if (targetServerState != null && isServerAlive == targetServerState) {
|
||||
targetServerState = null; // Transition complete!
|
||||
timeoutHandler.removeCallbacks(timeoutRunnable); // Cancel safety net
|
||||
runOnUiThread(() -> btnServerControl.stopProgress()); // Unlock button
|
||||
}
|
||||
|
||||
if (vpnOn && boxAlive) {
|
||||
currentTargetUrl = "http://box/home";
|
||||
} else if (localAlive) {
|
||||
|
|
@ -818,120 +840,90 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
|
|||
boolean isVpnActive = prefs.getEnable();
|
||||
boolean isWatchdogOn = prefs.getWatchdogEnable();
|
||||
|
||||
// Draw island
|
||||
// Draw island (Tunnel LED colors)
|
||||
if (dashboardManager != null) {
|
||||
dashboardManager.setTunnelState(isVpnActive, isProxyDegraded);
|
||||
}
|
||||
|
||||
// Draw main button
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, isServerAlive ? R.color.btn_vpn_on : R.color.btn_vpn_on_dim));
|
||||
// Draw main VPN button (ESPW)
|
||||
if (!isServerAlive) {
|
||||
// Lock and dim the VPN button if there is no server to connect to
|
||||
button_control.setEnabled(false);
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on_dim));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off_dim));
|
||||
}
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, isServerAlive ? R.color.btn_vpn_off : R.color.btn_vpn_off_dim));
|
||||
// Unlock if server is alive
|
||||
button_control.setEnabled(true);
|
||||
if (isVpnActive) {
|
||||
button_control.setText(R.string.control_disable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on));
|
||||
} else {
|
||||
button_control.setText(R.string.control_enable);
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off));
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Draw Explore Content button
|
||||
// Draw Explore Content button
|
||||
// Ensure it is ALWAYS visible, never GONE
|
||||
button_browse_content.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!isServerAlive) {
|
||||
// State 1: Stopped
|
||||
// State 1: Stopped (Greyed out)
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(false);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_disabled));
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
button_browse_content.setTextColor(Color.parseColor("#888888")); // Texto grisáceo apagado
|
||||
button_browse_content.setTextColor(Color.parseColor("#888888"));
|
||||
} else if (isNegotiating) {
|
||||
// State 2: Negotiating
|
||||
// State 3: Negotiating
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
// (El latido ya maneja la opacidad al 100%)
|
||||
} else {
|
||||
// State: Alive
|
||||
stopExplorePulse();
|
||||
button_browse_content.setEnabled(true);
|
||||
button_browse_content.setTextColor(Color.WHITE);
|
||||
button_browse_content.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_ready));
|
||||
|
||||
if (isVpnActive && !isProxyDegraded) {
|
||||
// State 5: All good
|
||||
button_browse_content.setAlpha(1.0f);
|
||||
} else {
|
||||
// State 2: local or state 4
|
||||
button_browse_content.setAlpha(0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
// FUSION LOGIC - Watchdog
|
||||
btnServerControl.setAlpha(1.0f);
|
||||
if (isServerAlive) {
|
||||
btnServerControl.setText("🛑 Stop Server");
|
||||
if (isWatchdogOn) {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
} else {
|
||||
if (fusionAnimator == null || !fusionAnimator.isRunning()) deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_danger));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
|
||||
}
|
||||
} else {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setText("🚀 Launch Server");
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_success));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, isWatchdogOn ? R.color.btn_watchdog_on : R.color.btn_watchdog_off));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUIColorsAndVisibility(boolean isServerAlive) {
|
||||
boolean isVpnActive = prefs.getEnable();
|
||||
|
||||
if (!isServerAlive) {
|
||||
button_control.setEnabled(false); // Disable ESPW click
|
||||
button_browse_content.setVisibility(View.GONE);
|
||||
stopExplorePulse(); // We stop the animation if the server dies
|
||||
|
||||
if (isVpnActive) {
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on_dim));
|
||||
} else {
|
||||
button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off_dim));
|
||||
}
|
||||
} else {
|
||||
button_control.setEnabled(true); // Enable ESPW click
|
||||
button_browse_content.setVisibility(View.VISIBLE);
|
||||
|
||||
// Heart rate and diluted color control
|
||||
if (isVpnActive) {
|
||||
button_browse_content.setAlpha(1.0f); // 100% Perfect state
|
||||
startExplorePulse();
|
||||
} else {
|
||||
stopExplorePulse();
|
||||
button_browse_content.setAlpha(0.6f); // Watered down fallback state
|
||||
}
|
||||
}
|
||||
// --- THE FUSION LOGIC (CASSETTE DECK) ---
|
||||
boolean isWatchdogOn = prefs.getWatchdogEnable();
|
||||
btnServerControl.setAlpha(1.0f); // Reset opacity
|
||||
|
||||
if (isServerAlive) {
|
||||
btnServerControl.setText("🛑 Stop Server");
|
||||
|
||||
if (isWatchdogOn) {
|
||||
// FUSION: Server alive + Watchdog active
|
||||
// DELETED the stopFusionPulse() from here so the animation can live!
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800")); // Bright border/background
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
} else {
|
||||
// Sever alive, without Watchdog
|
||||
if (fusionAnimator == null || !fusionAnimator.isRunning()) {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_danger)); // Red
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
|
||||
}
|
||||
// FUSION LOGIC (Watchdog & Server Control)
|
||||
if (targetServerState != null) {
|
||||
// STATE: COOL-OFF (Locked)
|
||||
btnServerControl.setAlpha(0.6f);
|
||||
btnServerControl.setText(serverTransitionText);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_explore_disabled));
|
||||
} else {
|
||||
// Server offline
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setText("🚀 Launch Server");
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_success)); // Verde
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, isWatchdogOn ? R.color.btn_watchdog_on : R.color.btn_watchdog_off));
|
||||
// STATE: NORMAL (Unlocked)
|
||||
btnServerControl.setAlpha(1.0f);
|
||||
if (isServerAlive) {
|
||||
btnServerControl.setText("🛑 Stop Server");
|
||||
if (isWatchdogOn) {
|
||||
deckContainer.setBackgroundColor(Color.parseColor("#44FF9800"));
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
|
||||
} else {
|
||||
if (fusionAnimator == null || !fusionAnimator.isRunning()) deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_danger));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
|
||||
}
|
||||
} else {
|
||||
deckContainer.setBackgroundColor(Color.TRANSPARENT);
|
||||
btnServerControl.setText("🚀 Launch Server");
|
||||
btnServerControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_success));
|
||||
watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, isWatchdogOn ? R.color.btn_watchdog_on : R.color.btn_watchdog_off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
package org.iiab.controller;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Path;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.appcompat.widget.AppCompatButton;
|
||||
|
||||
public class ProgressButton extends AppCompatButton {
|
||||
|
||||
private Paint progressPaint;
|
||||
private Paint progressBackgroundPaint;
|
||||
|
||||
private int progressColor;
|
||||
private int progressBackgroundColor;
|
||||
private Path clipPath;
|
||||
private RectF rectF;
|
||||
private float cornerRadius;
|
||||
private int progressHeight;
|
||||
|
||||
// Animation variables
|
||||
private float currentProgress = 0f;
|
||||
private boolean isRunning = false;
|
||||
private ValueAnimator animator;
|
||||
|
||||
public ProgressButton(Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
}
|
||||
|
||||
public ProgressButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public ProgressButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
if (attrs != null) {
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressButton, 0, 0);
|
||||
try {
|
||||
progressColor = a.getColor(R.styleable.ProgressButton_progressButtonColor, ContextCompat.getColor(context, R.color.btn_danger));
|
||||
progressBackgroundColor = a.getColor(R.styleable.ProgressButton_progressButtonBackgroundColor, Color.parseColor("#44888888"));
|
||||
progressHeight = a.getDimensionPixelSize(R.styleable.ProgressButton_progressButtonHeight, (int) (6 * getResources().getDisplayMetrics().density));
|
||||
// Note: We no longer read 'duration' from XML because the animation is infinite.
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
} else {
|
||||
// Safe defaults
|
||||
progressColor = ContextCompat.getColor(context, R.color.btn_danger);
|
||||
progressBackgroundColor = Color.parseColor("#44888888");
|
||||
progressHeight = (int) (6 * getResources().getDisplayMetrics().density);
|
||||
}
|
||||
|
||||
progressPaint = new Paint();
|
||||
progressPaint.setColor(progressColor);
|
||||
progressPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
progressBackgroundPaint = new Paint();
|
||||
progressBackgroundPaint.setColor(progressBackgroundColor);
|
||||
progressBackgroundPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
// Initialize clipping path variables
|
||||
clipPath = new Path();
|
||||
rectF = new RectF();
|
||||
cornerRadius = 8 * getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw the background and text first
|
||||
super.onDraw(canvas);
|
||||
|
||||
// Draw the progress bar constrained by the button's rounded corners
|
||||
if (progressHeight > 0 && isRunning) {
|
||||
int buttonWidth = getWidth();
|
||||
int buttonHeight = getHeight();
|
||||
|
||||
// Calculate width based on the current animated float (0.0f to 1.0f)
|
||||
int progressWidth = (int) (buttonWidth * currentProgress);
|
||||
|
||||
// 1. Prepare the rounded mask (matches the button's bounds)
|
||||
rectF.set(0, 0, buttonWidth, buttonHeight);
|
||||
clipPath.reset();
|
||||
clipPath.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
|
||||
|
||||
// 2. Save canvas state and apply the mask
|
||||
canvas.save();
|
||||
canvas.clipPath(clipPath);
|
||||
|
||||
// 3. Draw the tracks
|
||||
canvas.drawRect(0, buttonHeight - progressHeight, buttonWidth, buttonHeight, progressBackgroundPaint);
|
||||
canvas.drawRect(0, buttonHeight - progressHeight, progressWidth, buttonHeight, progressPaint);
|
||||
|
||||
// 4. Restore canvas
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an infinite cyclic animation (fills and empties the bar).
|
||||
* Disables the button to prevent spam clicks.
|
||||
*/
|
||||
public void startProgress() {
|
||||
if (isRunning) return;
|
||||
isRunning = true;
|
||||
setEnabled(false); // Lock the button immediately
|
||||
|
||||
// Create an animator that goes from 0.0 to 1.0 (empty to full)
|
||||
animator = ValueAnimator.ofFloat(0f, 1f);
|
||||
animator.setDuration(1200); // 1.2 seconds per sweep
|
||||
animator.setRepeatMode(ValueAnimator.REVERSE); // Fill up, then empty down
|
||||
animator.setRepeatCount(ValueAnimator.INFINITE); // Never stop until commanded
|
||||
|
||||
animator.addUpdateListener(animation -> {
|
||||
currentProgress = (float) animation.getAnimatedValue();
|
||||
invalidate(); // Force redraw on every frame
|
||||
});
|
||||
|
||||
animator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the animation, clears the bar, and unlocks the button.
|
||||
* To be called by the Controller when the backend confirms the state change.
|
||||
*/
|
||||
public void stopProgress() {
|
||||
if (animator != null && animator.isRunning()) {
|
||||
animator.cancel();
|
||||
}
|
||||
isRunning = false;
|
||||
setEnabled(true); // Unlock button
|
||||
currentProgress = 0f; // Reset width
|
||||
invalidate(); // Clear the bar visually
|
||||
}
|
||||
}
|
||||
|
|
@ -343,7 +343,7 @@
|
|||
android:baselineAligned="false"
|
||||
android:weightSum="2">
|
||||
|
||||
<Button
|
||||
<org.iiab.controller.ProgressButton
|
||||
android:id="@+id/btn_server_control"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
|
|
@ -355,7 +355,12 @@
|
|||
android:textColor="#FFFFFF"
|
||||
android:background="@drawable/rounded_button"
|
||||
android:backgroundTint="@color/btn_success"
|
||||
android:textAllCaps="false"/>
|
||||
android:textAllCaps="false"
|
||||
|
||||
app:progressButtonHeight="6dp"
|
||||
app:progressButtonDuration="@integer/server_cool_off_duration_ms"
|
||||
app:progressButtonColor="#FF9800" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/watchdog_control"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
|||
|
|
@ -2,4 +2,10 @@
|
|||
<resources>
|
||||
<attr name="sectionBackground" format="color" />
|
||||
<attr name="sectionHeaderBackground" format="color" />
|
||||
<declare-styleable name="ProgressButton">
|
||||
<attr name="progressButtonColor" format="color" />
|
||||
<attr name="progressButtonBackgroundColor" format="color" />
|
||||
<attr name="progressButtonHeight" format="dimension" />
|
||||
<attr name="progressButtonDuration" format="integer" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="server_cool_off_duration_ms">15000</integer>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue