mirror of
https://github.com/izzy2lost/PSX2.git
synced 2026-07-05 15:18:36 -07:00
New setup wizard - groundwork for angle on Mali devices
This commit is contained in:
@@ -115,6 +115,7 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.4.0'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.activity:activity:1.12.1'
|
||||
implementation 'androidx.core:core-ktx:1.15.0'
|
||||
|
||||
@@ -41,6 +41,9 @@ namespace AndroidDeviceDetection
|
||||
std::string board = GetSystemProperty("ro.product.board");
|
||||
std::string platform = GetSystemProperty("ro.board.platform");
|
||||
|
||||
Console.WriteLn("Device Detection: hardware='%s', board='%s', platform='%s'",
|
||||
hardware.c_str(), board.c_str(), platform.c_str());
|
||||
|
||||
// Convert to lowercase for comparison
|
||||
auto toLower = [](std::string str) {
|
||||
for (char& c : str) c = std::tolower(c);
|
||||
@@ -65,6 +68,9 @@ namespace AndroidDeviceDetection
|
||||
std::string board = GetSystemProperty("ro.product.board");
|
||||
std::string platform = GetSystemProperty("ro.board.platform");
|
||||
|
||||
Console.WriteLn("Device Detection: hardware='%s', board='%s', platform='%s'",
|
||||
hardware.c_str(), board.c_str(), platform.c_str());
|
||||
|
||||
auto toLower = [](std::string str) {
|
||||
for (char& c : str) c = std::tolower(c);
|
||||
return str;
|
||||
@@ -99,13 +105,40 @@ namespace AndroidDeviceDetection
|
||||
|
||||
// Check for other vendors via hardware string
|
||||
std::string hardware = GetSystemProperty("ro.hardware");
|
||||
if (hardware.find("exynos") != std::string::npos)
|
||||
std::string manufacturer = GetSystemProperty("ro.product.manufacturer");
|
||||
|
||||
auto toLower = [](std::string str) {
|
||||
for (char& c : str) c = std::tolower(c);
|
||||
return str;
|
||||
};
|
||||
|
||||
hardware = toLower(hardware);
|
||||
manufacturer = toLower(manufacturer);
|
||||
|
||||
// Samsung Exynos devices (Mali GPU)
|
||||
if (hardware.find("exynos") != std::string::npos ||
|
||||
hardware.find("universal") != std::string::npos ||
|
||||
(manufacturer.find("samsung") != std::string::npos && hardware.find("samsungexynos") != std::string::npos))
|
||||
{
|
||||
Console.WriteLn("Detected Samsung Exynos (Mali GPU)");
|
||||
return GPUVendor::ARM;
|
||||
}
|
||||
|
||||
Console.WriteLn("Unknown GPU vendor, hardware: %s", hardware.c_str());
|
||||
// Kirin devices (Mali GPU)
|
||||
if (hardware.find("kirin") != std::string::npos || hardware.find("hi") == 0)
|
||||
{
|
||||
Console.WriteLn("Detected HiSilicon Kirin (Mali GPU)");
|
||||
return GPUVendor::ARM;
|
||||
}
|
||||
|
||||
// Rockchip devices (Mali GPU)
|
||||
if (hardware.find("rk") == 0 || hardware.find("rockchip") != std::string::npos)
|
||||
{
|
||||
Console.WriteLn("Detected Rockchip (Mali GPU)");
|
||||
return GPUVendor::ARM;
|
||||
}
|
||||
|
||||
Console.WriteLn("Unknown GPU vendor, hardware: %s, manufacturer: %s", hardware.c_str(), manufacturer.c_str());
|
||||
return GPUVendor::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,27 @@
|
||||
#include "GS/Renderers/OpenGL/GLContextEGL.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#ifdef __ANDROID__
|
||||
#include "AndroidDeviceDetection.h"
|
||||
#endif
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
// NDK headers may lack ANGLE platform defines; provide fallbacks so we can request ANGLE.
|
||||
#ifndef EGL_PLATFORM_ANGLE_ANGLE
|
||||
#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
|
||||
#endif
|
||||
#ifndef EGL_PLATFORM_ANGLE_TYPE_ANGLE
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
|
||||
#endif
|
||||
#ifndef EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x320F
|
||||
#endif
|
||||
#ifndef EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE
|
||||
#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209
|
||||
#endif
|
||||
#ifndef EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE
|
||||
#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -43,12 +64,36 @@ bool GLContextEGL::Initialize(const Version* versions_to_try, size_t num_version
|
||||
return false;
|
||||
|
||||
int egl_major, egl_minor;
|
||||
retry_initialize:
|
||||
if (!eglInitialize(m_display, &egl_major, &egl_minor))
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
if (m_tried_angle_display && m_used_angle_display)
|
||||
{
|
||||
const EGLint error = eglGetError();
|
||||
Console.Warning("ANGLE: eglInitialize() failed with error 0x%x, retrying with native Mali driver", error);
|
||||
m_used_angle_display = false;
|
||||
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
||||
if (m_display != EGL_NO_DISPLAY)
|
||||
goto retry_initialize;
|
||||
}
|
||||
#endif
|
||||
Console.Error("eglInitialize() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
Console.WriteLn("EGL Version: %d.%d", egl_major, egl_minor);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (m_used_angle_display)
|
||||
{
|
||||
Console.WriteLn("ANGLE: Successfully initialized ANGLE EGL display");
|
||||
Console.WriteLn("ANGLE: OpenGL renderer will use ANGLE (GL ES -> Vulkan translation)");
|
||||
}
|
||||
else if (m_tried_angle_display)
|
||||
{
|
||||
Console.WriteLn("EGL: Using native Mali OpenGL ES driver (ANGLE not available)");
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS);
|
||||
if (extensions)
|
||||
@@ -67,7 +112,62 @@ bool GLContextEGL::Initialize(const Version* versions_to_try, size_t num_version
|
||||
|
||||
bool GLContextEGL::SetDisplay()
|
||||
{
|
||||
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
||||
#ifdef __ANDROID__
|
||||
const AndroidDeviceDetection::GPUVendor vendor = AndroidDeviceDetection::DetectGPUVendor();
|
||||
const bool prefer_angle = (vendor == AndroidDeviceDetection::GPUVendor::ARM);
|
||||
m_tried_angle_display = prefer_angle;
|
||||
|
||||
Console.WriteLn("EGL: GPU Vendor detected: %d (ARM=%d), prefer_angle=%d",
|
||||
static_cast<int>(vendor), static_cast<int>(AndroidDeviceDetection::GPUVendor::ARM), prefer_angle);
|
||||
|
||||
if (prefer_angle)
|
||||
{
|
||||
Console.WriteLn("ANGLE: Mali GPU detected, attempting to use ANGLE with Vulkan backend");
|
||||
|
||||
auto get_platform_display = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
|
||||
eglGetProcAddress("eglGetPlatformDisplayEXT"));
|
||||
|
||||
if (get_platform_display)
|
||||
{
|
||||
Console.WriteLn("ANGLE: eglGetPlatformDisplayEXT found, requesting ANGLE display");
|
||||
|
||||
const EGLint attribs[] = {
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
m_display = get_platform_display(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, attribs);
|
||||
if (m_display != EGL_NO_DISPLAY)
|
||||
{
|
||||
Console.WriteLn("ANGLE: Successfully created ANGLE display with Vulkan backend for Mali GPU");
|
||||
Console.WriteLn("ANGLE: OpenGL ES calls will be translated to Vulkan");
|
||||
m_used_angle_display = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const EGLint error = eglGetError();
|
||||
Console.Warning("ANGLE: eglGetPlatformDisplayEXT failed with error 0x%x, falling back to default EGL", error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("ANGLE: eglGetPlatformDisplayEXT not available (ANGLE libraries may not be loaded)");
|
||||
Console.Warning("ANGLE: Falling back to native Mali OpenGL ES driver");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn("EGL: Non-Mali GPU detected, using native OpenGL ES driver");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_display)
|
||||
{
|
||||
Console.WriteLn("EGL: Using default eglGetDisplay()");
|
||||
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
||||
}
|
||||
|
||||
if (!m_display)
|
||||
{
|
||||
Console.Error("eglGetDisplay() failed: %d", eglGetError());
|
||||
|
||||
@@ -48,4 +48,8 @@ protected:
|
||||
EGLConfig m_config = {};
|
||||
|
||||
bool m_supports_surfaceless = false;
|
||||
#ifdef __ANDROID__
|
||||
bool m_tried_angle_display = false;
|
||||
bool m_used_angle_display = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -648,6 +648,10 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
|
||||
const std::string vendor_lower = to_lower(vendor_str);
|
||||
const std::string renderer_lower = to_lower(renderer_str);
|
||||
|
||||
// Detect Mali GPU
|
||||
bool vendor_id_arm_mali = (vendor_lower.find("arm") != std::string::npos ||
|
||||
renderer_lower.find("mali") != std::string::npos);
|
||||
|
||||
if (vendor_lower.find("advanced micro devices") != std::string::npos ||
|
||||
vendor_lower.find("ati technologies inc.") != std::string::npos ||
|
||||
vendor_lower.find("ati") != std::string::npos)
|
||||
@@ -665,6 +669,10 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
|
||||
Console.WriteLn(Color_StrongBlue, "GL: Intel GPU detected.");
|
||||
//vendor_id_intel = true;
|
||||
}
|
||||
else if (vendor_id_arm_mali)
|
||||
{
|
||||
Console.WriteLn(Color_StrongYellow, "GL: ARM Mali GPU detected - applying workarounds");
|
||||
}
|
||||
GLint major_gl = 0;
|
||||
GLint minor_gl = 0;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major_gl);
|
||||
@@ -746,12 +754,31 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
|
||||
} else {
|
||||
buggy_pbo = !GLAD_GL_EXT_buffer_storage;
|
||||
}
|
||||
|
||||
// Mali GPU workarounds - force PBO off and disable problematic features
|
||||
if (vendor_id_arm_mali)
|
||||
{
|
||||
Console.WriteLn(Color_StrongYellow, "GL: Applying Mali GPU workarounds:");
|
||||
Console.WriteLn(" - Disabling framebuffer fetch");
|
||||
Console.WriteLn(" - Enabling texture barriers");
|
||||
Console.WriteLn(" - Disabling vertex shader expansion");
|
||||
Console.WriteLn(" - Disabling PBO for texture uploads");
|
||||
Console.WriteLn(" - Disabling PBO for texture downloads");
|
||||
Console.WriteLn(" - Disabling point expand");
|
||||
|
||||
buggy_pbo = true;
|
||||
m_disable_download_pbo = true;
|
||||
|
||||
Console.WriteLn("GL: Mali workarounds applied. Textures will use direct upload path.");
|
||||
}
|
||||
|
||||
if (buggy_pbo)
|
||||
Console.Warning("GL: Not using PBOs for texture uploads because buffer_storage is unavailable.");
|
||||
|
||||
// Give the user the option to disable PBO usage for downloads.
|
||||
// Most drivers seem to be faster with PBO.
|
||||
m_disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
|
||||
if (!vendor_id_arm_mali)
|
||||
m_disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
|
||||
if (m_disable_download_pbo)
|
||||
Console.Warning("GL: Not using PBOs for texture downloads, this may reduce performance.");
|
||||
|
||||
@@ -760,6 +787,14 @@ bool GSDeviceOGL::CheckFeatures(bool& buggy_pbo)
|
||||
m_features.primitive_id = true;
|
||||
|
||||
m_features.framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch;
|
||||
|
||||
// Disable framebuffer fetch on Mali - causes 2D graphics issues
|
||||
if (vendor_id_arm_mali && m_features.framebuffer_fetch)
|
||||
{
|
||||
Console.WriteLn("GL: Disabling framebuffer fetch on Mali GPU (causes rendering issues)");
|
||||
m_features.framebuffer_fetch = false;
|
||||
}
|
||||
|
||||
if (m_features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
|
||||
{
|
||||
Host::AddOSDMessage(
|
||||
|
||||
@@ -9,7 +9,20 @@ import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class NativeApp {
|
||||
private static boolean angleLoaded = false;
|
||||
|
||||
static {
|
||||
try {
|
||||
// Try shipping ANGLE (EGL/GLES-over-Vulkan). If missing, we'll fall back to system drivers.
|
||||
System.loadLibrary("EGL_angle");
|
||||
System.loadLibrary("GLESv2_angle");
|
||||
angleLoaded = true;
|
||||
android.util.Log.i("NativeApp", "ANGLE libraries loaded successfully (EGL_angle, GLESv2_angle)");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
// Optional: not all builds will bundle ANGLE.
|
||||
android.util.Log.w("NativeApp", "ANGLE libraries not found, will use native OpenGL ES driver: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
System.loadLibrary("emucore");
|
||||
hasNoNativeBinary = false;
|
||||
@@ -17,6 +30,10 @@ public class NativeApp {
|
||||
hasNoNativeBinary = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAngleLoaded() {
|
||||
return angleLoaded;
|
||||
}
|
||||
|
||||
public static boolean hasNoNativeBinary;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="135"
|
||||
android:startColor="@color/md_theme_primaryContainer"
|
||||
android:centerColor="@color/md_theme_tertiary"
|
||||
android:endColor="@color/md_theme_tertiaryContainer"
|
||||
android:type="linear" />
|
||||
<corners android:bottomLeftRadius="24dp" android:bottomRightRadius="24dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,333L760,333L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,333ZM200,547L760,547L760,413L200,413L200,547ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,627L200,627L200,760Q200,760 200,760Q200,760 200,760ZM240,306L240,226L320,226L320,306L240,306ZM240,520L240,440L320,440L320,520L240,520ZM240,734L240,654L320,654L320,734L240,734Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M360,600L360,360L600,360L600,600L360,600ZM440,520L520,520L520,440L440,440L440,520ZM360,840L360,760L280,760Q247,760 223.5,736.5Q200,713 200,680L200,600L120,600L120,520L200,520L200,440L120,440L120,360L200,360L200,280Q200,247 223.5,223.5Q247,200 280,200L360,200L360,120L440,120L440,200L520,200L520,120L600,120L600,200L680,200Q713,200 736.5,223.5Q760,247 760,280L760,360L840,360L840,440L760,440L760,520L840,520L840,600L760,600L760,680Q760,713 736.5,736.5Q713,760 680,760L600,760L600,840L520,840L520,760L440,760L440,840L360,840ZM680,680Q680,680 680,680Q680,680 680,680L680,280Q680,280 680,280Q680,280 680,280L280,280Q280,280 280,280Q280,280 280,280L280,680Q280,680 280,680Q280,680 280,680L680,680ZM480,480L480,480L480,480L480,480Z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<size android:width="16dp" android:height="16dp" />
|
||||
<solid android:color="@color/md_theme_tertiary" />
|
||||
</shape>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<size android:width="16dp" android:height="16dp" />
|
||||
<solid android:color="@color/md_theme_outlineVariant" />
|
||||
</shape>
|
||||
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/md_theme_surface"
|
||||
android:fitsSystemWindows="false">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/header_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="180dp"
|
||||
android:background="@drawable/bg_setup_intro_header"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="48dp"
|
||||
android:paddingBottom="20dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_step"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Step 1 of 3"
|
||||
android:textColor="@color/md_theme_onPrimary"
|
||||
android:textSize="12sp"
|
||||
android:alpha="0.85"
|
||||
android:paddingBottom="4dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Welcome to PSX2"
|
||||
android:textColor="@color/md_theme_onPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Three quick steps to start playing"
|
||||
android:textColor="@color/md_theme_onPrimary"
|
||||
android:textSize="14sp"
|
||||
android:alpha="0.9"
|
||||
android:paddingTop="6dp" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/setup_pager"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/swipe_hint" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/swipe_hint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="← Swipe to navigate →"
|
||||
android:textSize="13sp"
|
||||
android:textColor="@color/md_theme_tertiary"
|
||||
android:textStyle="bold"
|
||||
android:alpha="0.85"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/indicator_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/indicator_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btn_next"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_next"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Next"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:backgroundTint="@color/md_theme_tertiaryContainer"
|
||||
android:textColor="@color/md_theme_onTertiaryContainer"
|
||||
app:iconGravity="textStart"
|
||||
app:iconTint="@color/md_theme_onTertiaryContainer"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@color/md_theme_surfaceContainer">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:tint="@color/md_theme_tertiary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_step_chip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:text="STEP"
|
||||
android:textAllCaps="true"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/md_theme_onSurface"
|
||||
android:background="@color/md_theme_surfaceContainerHigh"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="Title"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/md_theme_onSurface"
|
||||
android:gravity="center"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_step_chip"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_description"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Description"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/md_theme_onSurfaceVariant"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/action_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_description"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_action"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Do it"
|
||||
android:textAllCaps="false"
|
||||
android:backgroundTint="@color/md_theme_tertiaryContainer"
|
||||
android:textColor="@color/md_theme_onTertiaryContainer"
|
||||
app:iconTint="@color/md_theme_tertiary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:text="Pending"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/md_theme_onSurface"
|
||||
android:background="@drawable/bg_status_badge"
|
||||
android:backgroundTint="@color/md_theme_outlineVariant"
|
||||
android:layout_gravity="center_vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
Reference in New Issue
Block a user