forked from MeloNX/MeloNX
android - add basic user management
This commit is contained in:
parent
8e2eb5fd26
commit
ba745514a1
@ -18,6 +18,7 @@ using Silk.NET.Vulkan.Extensions.KHR;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
@ -95,6 +96,12 @@ namespace LibRyujinx
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceReloadFilesystem")]
|
||||||
|
public static void JniReloadFileSystem()
|
||||||
|
{
|
||||||
|
SwitchDevice?.ReloadFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInitialize")]
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceInitialize")]
|
||||||
public static JBoolean JniInitializeDeviceNative(JEnvRef jEnv,
|
public static JBoolean JniInitializeDeviceNative(JEnvRef jEnv,
|
||||||
JObjectLocalRef jObj,
|
JObjectLocalRef jObj,
|
||||||
@ -503,6 +510,89 @@ namespace LibRyujinx
|
|||||||
return ConnectGamepad(index);
|
return ConnectGamepad(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetOpenedUser")]
|
||||||
|
public static JStringLocalRef JniGetOpenedUser(JEnvRef jEnv, JObjectLocalRef jObj)
|
||||||
|
{
|
||||||
|
var userId = GetOpenedUser();
|
||||||
|
|
||||||
|
return CreateString(jEnv, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserPicture")]
|
||||||
|
public static JStringLocalRef JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
|
||||||
|
return CreateString(jEnv, GetUserPicture(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserPicture")]
|
||||||
|
public static void JniGetUserPicture(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef picturePtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
var picture = GetString(jEnv, picturePtr) ?? "";
|
||||||
|
|
||||||
|
SetUserPicture(userId, picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetUserName")]
|
||||||
|
public static JStringLocalRef JniGetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
|
||||||
|
return CreateString(jEnv, GetUserName(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userSetUserName")]
|
||||||
|
public static void JniSetUserName(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr, JStringLocalRef userNamePtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
var userName = GetString(jEnv, userNamePtr) ?? "";
|
||||||
|
|
||||||
|
SetUserName(userId, userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userGetAllUsers")]
|
||||||
|
public static JArrayLocalRef JniGetAllUsers(JEnvRef jEnv, JObjectLocalRef jObj)
|
||||||
|
{
|
||||||
|
var users = GetAllUsers();
|
||||||
|
|
||||||
|
return CreateStringArray(jEnv, users.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userAddUser")]
|
||||||
|
public static void JniAddUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userNamePtr, JStringLocalRef picturePtr)
|
||||||
|
{
|
||||||
|
var userName = GetString(jEnv, userNamePtr) ?? "";
|
||||||
|
var picture = GetString(jEnv, picturePtr) ?? "";
|
||||||
|
|
||||||
|
AddUser(userName, picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userDeleteUser")]
|
||||||
|
public static void JniDeleteUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
|
||||||
|
DeleteUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userOpenUser")]
|
||||||
|
public static void JniOpenUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
|
||||||
|
OpenUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_userCloseUser")]
|
||||||
|
public static void JniCloseUser(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef userIdPtr)
|
||||||
|
{
|
||||||
|
var userId = GetString(jEnv, userIdPtr) ?? "";
|
||||||
|
|
||||||
|
CloseUser(userId);
|
||||||
|
}
|
||||||
|
|
||||||
private static FileStream OpenFile(int descriptor)
|
private static FileStream OpenFile(int descriptor)
|
||||||
{
|
{
|
||||||
var safeHandle = new SafeFileHandle(descriptor, false);
|
var safeHandle = new SafeFileHandle(descriptor, false);
|
||||||
|
@ -17,6 +17,12 @@ namespace LibRyujinx
|
|||||||
return InitializeDevice(true, false, SystemLanguage.AmericanEnglish, RegionCode.USA, true, true, true, false, "UTC", false);
|
return InitializeDevice(true, false, SystemLanguage.AmericanEnglish, RegionCode.USA, true, true, true, false, "UTC", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[UnmanagedCallersOnly(EntryPoint = "device_reloadFilesystem")]
|
||||||
|
public static void ReloadFileSystem()
|
||||||
|
{
|
||||||
|
SwitchDevice?.ReloadFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool InitializeDevice(bool isHostMapped,
|
public static bool InitializeDevice(bool isHostMapped,
|
||||||
bool useNce,
|
bool useNce,
|
||||||
SystemLanguage systemLanguage,
|
SystemLanguage systemLanguage,
|
||||||
|
82
src/LibRyujinx/LibRyujinx.User.cs
Normal file
82
src/LibRyujinx/LibRyujinx.User.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LibRyujinx
|
||||||
|
{
|
||||||
|
public static partial class LibRyujinx
|
||||||
|
{
|
||||||
|
public static string GetOpenedUser()
|
||||||
|
{
|
||||||
|
var lastProfile = SwitchDevice?.AccountManager.LastOpenedUser;
|
||||||
|
|
||||||
|
return lastProfile?.UserId.ToString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserPicture(string userId)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
|
||||||
|
var user = SwitchDevice?.AccountManager.GetAllUsers().FirstOrDefault(x => x.UserId == uid);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var pic = user.Image;
|
||||||
|
|
||||||
|
return pic != null ? Convert.ToBase64String(pic) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetUserPicture(string userId, string picture)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
|
||||||
|
SwitchDevice?.AccountManager.SetUserImage(uid, Convert.FromBase64String(picture));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserName(string userId)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
|
||||||
|
var user = SwitchDevice?.AccountManager.GetAllUsers().FirstOrDefault(x => x.UserId == uid);
|
||||||
|
|
||||||
|
return user?.Name ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetUserName(string userId, string name)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
|
||||||
|
SwitchDevice?.AccountManager.SetUserName(uid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] GetAllUsers()
|
||||||
|
{
|
||||||
|
return SwitchDevice?.AccountManager.GetAllUsers().Select(x => x.UserId.ToString()).ToArray() ??
|
||||||
|
Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddUser(string userName, string picture)
|
||||||
|
{
|
||||||
|
SwitchDevice?.AccountManager.AddUser(userName, Convert.FromBase64String(picture));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeleteUser(string userId)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
SwitchDevice?.AccountManager.DeleteUser(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenUser(string userId)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
SwitchDevice?.AccountManager.OpenUser(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CloseUser(string userId)
|
||||||
|
{
|
||||||
|
var uid = new UserId(userId);
|
||||||
|
SwitchDevice?.AccountManager.CloseUser(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -724,6 +724,12 @@ namespace LibRyujinx
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ReloadFileSystem()
|
||||||
|
{
|
||||||
|
VirtualFileSystem.ReloadKeySet();
|
||||||
|
ContentManager = new ContentManager(VirtualFileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
internal void DisposeContext()
|
internal void DisposeContext()
|
||||||
{
|
{
|
||||||
EmulationContext?.Dispose();
|
EmulationContext?.Dispose();
|
||||||
|
@ -53,4 +53,14 @@ class RyujinxNative {
|
|||||||
external fun deviceSignalEmulationClose()
|
external fun deviceSignalEmulationClose()
|
||||||
external fun deviceGetDlcTitleId(path: String, ncaPath: String) : String
|
external fun deviceGetDlcTitleId(path: String, ncaPath: String) : String
|
||||||
external fun deviceGetDlcContentList(path: String, titleId: Long) : Array<String>
|
external fun deviceGetDlcContentList(path: String, titleId: Long) : Array<String>
|
||||||
|
external fun userGetOpenedUser() : String
|
||||||
|
external fun userGetUserPicture(userId: String) : String
|
||||||
|
external fun userSetUserPicture(userId: String, picture: String)
|
||||||
|
external fun userGetUserName(userId: String) : String
|
||||||
|
external fun userSetUserName(userId: String, userName: String)
|
||||||
|
external fun userGetAllUsers() : Array<String>
|
||||||
|
external fun userAddUser(username: String, picture: String)
|
||||||
|
external fun userDeleteUser(userId: String)
|
||||||
|
external fun userOpenUser(userId: String)
|
||||||
|
external fun userCloseUser(userId: String)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package org.ryujinx.android.views
|
package org.ryujinx.android.views
|
||||||
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@ -11,15 +13,16 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.wrapContentWidth
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Person
|
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
@ -47,7 +50,10 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@ -57,9 +63,11 @@ import coil.compose.AsyncImage
|
|||||||
import com.anggrayudi.storage.extension.launchOnUiThread
|
import com.anggrayudi.storage.extension.launchOnUiThread
|
||||||
import org.ryujinx.android.MainActivity
|
import org.ryujinx.android.MainActivity
|
||||||
import org.ryujinx.android.R
|
import org.ryujinx.android.R
|
||||||
|
import org.ryujinx.android.RyujinxNative
|
||||||
import org.ryujinx.android.viewmodels.GameModel
|
import org.ryujinx.android.viewmodels.GameModel
|
||||||
import org.ryujinx.android.viewmodels.HomeViewModel
|
import org.ryujinx.android.viewmodels.HomeViewModel
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Base64
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@ -84,6 +92,10 @@ class HomeViews {
|
|||||||
val refresh = remember {
|
val refresh = remember {
|
||||||
mutableStateOf(true)
|
mutableStateOf(true)
|
||||||
}
|
}
|
||||||
|
val native = RyujinxNative()
|
||||||
|
val user = native.userGetOpenedUser()
|
||||||
|
val decoder = Base64.getDecoder()
|
||||||
|
val pic = decoder.decode(native.userGetUserPicture(user))
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -115,10 +127,16 @@ class HomeViews {
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
|
navController?.navigate("user")
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Image(
|
||||||
Icons.Filled.Person,
|
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size).asImageBitmap(),
|
||||||
contentDescription = "Run"
|
contentDescription = "user image",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(52.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -16,6 +16,7 @@ class MainView {
|
|||||||
|
|
||||||
NavHost(navController = navController, startDestination = "home") {
|
NavHost(navController = navController, startDestination = "home") {
|
||||||
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
|
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
|
||||||
|
composable("user") { UserViews.Main(mainViewModel, navController) }
|
||||||
composable("settings") {
|
composable("settings") {
|
||||||
SettingViews.Main(
|
SettingViews.Main(
|
||||||
SettingsViewModel(
|
SettingsViewModel(
|
||||||
|
@ -0,0 +1,182 @@
|
|||||||
|
package org.ryujinx.android.views
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import org.ryujinx.android.RyujinxNative
|
||||||
|
import org.ryujinx.android.viewmodels.MainViewModel
|
||||||
|
import java.util.Base64
|
||||||
|
|
||||||
|
class UserViews {
|
||||||
|
companion object {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun Main(viewModel: MainViewModel? = null, navController: NavHostController? = null) {
|
||||||
|
val ryujinxNative = RyujinxNative()
|
||||||
|
val decoder = Base64.getDecoder()
|
||||||
|
val openedUser = remember {
|
||||||
|
mutableStateOf(ryujinxNative.userGetOpenedUser())
|
||||||
|
}
|
||||||
|
|
||||||
|
val openedUserPic = remember {
|
||||||
|
mutableStateOf(decoder.decode(ryujinxNative.userGetUserPicture(openedUser.value)))
|
||||||
|
}
|
||||||
|
val openedUserName = remember {
|
||||||
|
mutableStateOf(ryujinxNative.userGetUserName(openedUser.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
val userList = remember {
|
||||||
|
mutableListOf("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
userList.clear()
|
||||||
|
userList.addAll(ryujinxNative.userGetAllUsers())
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh()
|
||||||
|
|
||||||
|
Scaffold(modifier = Modifier.fillMaxSize(),
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(title = {
|
||||||
|
Text(text = "Users")
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = {
|
||||||
|
viewModel?.navController?.popBackStack()
|
||||||
|
}) {
|
||||||
|
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}) { contentPadding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(contentPadding)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(text = "Selected user")
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
bitmap = BitmapFactory.decodeByteArray(
|
||||||
|
openedUserPic.value,
|
||||||
|
0,
|
||||||
|
openedUserPic.value.size
|
||||||
|
).asImageBitmap(),
|
||||||
|
contentDescription = "selected image",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(96.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(text = openedUserName.value)
|
||||||
|
Text(text = openedUser.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(text = "Available Users")
|
||||||
|
IconButton(onClick = {
|
||||||
|
refresh()
|
||||||
|
}) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Refresh,
|
||||||
|
contentDescription = "refresh users"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Adaptive(minSize = 96.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(4.dp)
|
||||||
|
) {
|
||||||
|
items(userList) { user ->
|
||||||
|
val pic = decoder.decode(ryujinxNative.userGetUserPicture(user))
|
||||||
|
val name = ryujinxNative.userGetUserName(user)
|
||||||
|
Image(
|
||||||
|
bitmap = BitmapFactory.decodeByteArray(pic, 0, pic.size)
|
||||||
|
.asImageBitmap(),
|
||||||
|
contentDescription = "selected image",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(4.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {
|
||||||
|
ryujinxNative.userOpenUser(user)
|
||||||
|
openedUser.value = user
|
||||||
|
openedUserPic.value = pic
|
||||||
|
openedUserName.value = name
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun Preview() {
|
||||||
|
UserViews.Main()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user