441 lines
15 KiB
C#
441 lines
15 KiB
C#
|
using System;
|
||
|
using Android.App;
|
||
|
using Android.Content;
|
||
|
using Android.OS;
|
||
|
using Android.Runtime;
|
||
|
using Android.Views;
|
||
|
using AndroidX.AppCompat.App;
|
||
|
using AndroidX.AppCompat.Widget;
|
||
|
using AndroidX.Core.View;
|
||
|
using AndroidX.DrawerLayout.Widget;
|
||
|
using Google.Android.Material.FloatingActionButton;
|
||
|
using Google.Android.Material.Navigation;
|
||
|
using Google.Android.Material.Snackbar;
|
||
|
using Tesses.YouTubeDownloader.Droid;
|
||
|
using Toolbar = AndroidX.AppCompat.Widget.Toolbar;
|
||
|
|
||
|
namespace TYTD.Mobile
|
||
|
{
|
||
|
public interface IServerItem
|
||
|
{
|
||
|
|
||
|
bool IsEditable {get;}
|
||
|
string Name {get;set;}
|
||
|
string InnerValue {get;set;}
|
||
|
bool Update();
|
||
|
void Delete();
|
||
|
}
|
||
|
public sealed class LocalItem : IServerItem
|
||
|
{
|
||
|
string name;
|
||
|
string value;
|
||
|
public LocalItem(string name,string value)
|
||
|
{
|
||
|
this.name = name;
|
||
|
this.value = value;
|
||
|
}
|
||
|
public bool IsEditable => false;
|
||
|
|
||
|
public string Name { get => name; set => throw new NotImplementedException(); }
|
||
|
public string InnerValue { get => value; set => throw new NotImplementedException(); }
|
||
|
|
||
|
public void Delete()
|
||
|
{
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
|
||
|
public bool Update()
|
||
|
{
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
}
|
||
|
public sealed class ServerItem : IServerItem
|
||
|
{
|
||
|
string oldname;
|
||
|
Dictionary<string,string> dict;
|
||
|
public ServerItem(Dictionary<string,string> dict,string name,string url)
|
||
|
{
|
||
|
oldname=name;
|
||
|
Name = name;
|
||
|
InnerValue=url;
|
||
|
this.dict=dict;
|
||
|
}
|
||
|
public bool IsEditable => true;
|
||
|
|
||
|
public string Name {get;set;}
|
||
|
|
||
|
public string InnerValue {
|
||
|
get;set;
|
||
|
}
|
||
|
public void Delete()
|
||
|
{
|
||
|
dict.Remove(oldname);
|
||
|
TYTDCommon.SaveServers(dict);
|
||
|
}
|
||
|
public bool Update()
|
||
|
{
|
||
|
if(oldname != Name)
|
||
|
{
|
||
|
dict.Remove(oldname);
|
||
|
dict.Add(Name,InnerValue);
|
||
|
TYTDCommon.SaveServers(dict);
|
||
|
return true;
|
||
|
}
|
||
|
dict[Name]=InnerValue;
|
||
|
TYTDCommon.SaveServers(dict);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
public static class ServerItemHandler
|
||
|
{
|
||
|
public static string AddUnique<T>(this Dictionary<string,T> items,string key,T value)
|
||
|
{
|
||
|
string keyOut=key;
|
||
|
int i = 2;
|
||
|
while(items.ContainsKey(keyOut))
|
||
|
{
|
||
|
keyOut = $"{key} ({i++})";
|
||
|
}
|
||
|
items.Add(keyOut,value);
|
||
|
return keyOut;
|
||
|
}
|
||
|
private static IEnumerable<string> getNames(this IEnumerable<IServerItem> items)
|
||
|
{
|
||
|
foreach(var item in items)
|
||
|
{
|
||
|
yield return item.Name;
|
||
|
}
|
||
|
}
|
||
|
public static string[] GetNames(this IEnumerable<IServerItem> items)
|
||
|
{
|
||
|
return items.getNames().ToArray();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Activity(Label = "@string/servers_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = false)]
|
||
|
public class ServersActivity : AppCompatActivity
|
||
|
{
|
||
|
public void UpdateItem(IServerItem item)
|
||
|
{
|
||
|
var builder=new AndroidX.AppCompat.App.AlertDialog.Builder(this);
|
||
|
builder.SetView(Resource.Layout.edit_server);
|
||
|
EditText? name =null;
|
||
|
EditText? url=null;
|
||
|
Button? sftp=null;
|
||
|
AndroidX.AppCompat.App.AlertDialog? alert=null;
|
||
|
builder.SetNegativeButton(Resource.String.cancel_text,(sender,e)=>{
|
||
|
if(alert != null) alert.Dismiss();
|
||
|
});
|
||
|
builder.SetTitle(Resource.String.edit_server_text);
|
||
|
builder.SetPositiveButton(Resource.String.ok_text,(sender,e)=>{
|
||
|
if(svrs != null&&name!= null && url !=null && !string.IsNullOrWhiteSpace(name.Text) && !string.IsNullOrWhiteSpace(url.Text)){
|
||
|
item.Name = name.Text;
|
||
|
item.InnerValue = url.Text;
|
||
|
if(item.Update())
|
||
|
|
||
|
UpdateLV();
|
||
|
}
|
||
|
if(alert != null) alert.Dismiss();
|
||
|
});
|
||
|
|
||
|
|
||
|
alert= builder.Show();
|
||
|
if(alert != null)
|
||
|
{
|
||
|
//this.GetExternalFilesDirs(null)
|
||
|
name=alert.FindViewById<EditText>(Resource.Id.server_name_field);
|
||
|
if(name!= null)
|
||
|
name.Text = item.Name;
|
||
|
url = alert.FindViewById<EditText>(Resource.Id.server_uri_field);
|
||
|
if(url != null)
|
||
|
url.Text = item.InnerValue;
|
||
|
sftp = alert.FindViewById<Button>(Resource.Id.server_ssh_button);
|
||
|
if(sftp != null)
|
||
|
{
|
||
|
sftp.Click += (sender,e)=>{
|
||
|
if(url != null)
|
||
|
{
|
||
|
SftpEditor(url);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
Dictionary<string,string> svrs=new();
|
||
|
List<IServerItem> items=new List<IServerItem>();
|
||
|
ListView? listView=null;
|
||
|
protected override void OnCreate(Bundle? savedInstanceState)
|
||
|
{
|
||
|
|
||
|
|
||
|
base.OnCreate(savedInstanceState);
|
||
|
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
||
|
SetContentView(Resource.Layout.activity_servers);
|
||
|
|
||
|
Toolbar? toolbar = FindViewById<Toolbar>(Resource.Id.toolbar_servers);
|
||
|
if(toolbar != null)
|
||
|
{
|
||
|
SetSupportActionBar(toolbar);
|
||
|
SupportActionBar?.SetDisplayHomeAsUpEnabled(true);
|
||
|
SupportActionBar?.SetDisplayShowHomeEnabled(true);
|
||
|
|
||
|
}
|
||
|
FloatingActionButton? fab = FindViewById<FloatingActionButton>(Resource.Id.add_server);
|
||
|
|
||
|
if(fab != null)
|
||
|
fab.Click += FabOnClick;
|
||
|
|
||
|
listView = FindViewById<ListView>(Resource.Id.server_list);
|
||
|
if(listView!=null)
|
||
|
{
|
||
|
listView.ItemClick += (sender,e)=>{
|
||
|
IServerItem item = items[e.Position];
|
||
|
|
||
|
Android.Widget.PopupMenu menu=new Android.Widget.PopupMenu(this,e.View);
|
||
|
menu.MenuItemClick += (sender2,e2)=>{
|
||
|
if(e2.Item != null)
|
||
|
switch(e2.Item.ItemId)
|
||
|
{
|
||
|
case Resource.Id.action_storage_set:
|
||
|
case Resource.Id.action_storage_setne:
|
||
|
TYTDCommon.AppStorageLocation = item.InnerValue;
|
||
|
//set storage
|
||
|
break;
|
||
|
case Resource.Id.action_download_set:
|
||
|
case Resource.Id.action_download_setne:
|
||
|
TYTDCommon.AppDownloadLocation = item.InnerValue;
|
||
|
//set download to
|
||
|
break;
|
||
|
case Resource.Id.action_edit_server:
|
||
|
if(item.IsEditable){
|
||
|
UpdateItem(item);
|
||
|
}
|
||
|
break;
|
||
|
case Resource.Id.action_delete_server:
|
||
|
if(item.IsEditable){
|
||
|
item.Delete();
|
||
|
UpdateLV();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
};
|
||
|
menu.Inflate(item.IsEditable ? Resource.Menu.menu_server_editable : Resource.Menu.menu_server_not_editable);
|
||
|
menu.Show();
|
||
|
|
||
|
};
|
||
|
UpdateLV();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
public void UpdateLV()
|
||
|
{
|
||
|
if(listView != null)
|
||
|
{
|
||
|
items.Clear();
|
||
|
foreach(var storage in TYTDCommon.GetStorageDevices())
|
||
|
{
|
||
|
items.Add(new LocalItem(storage.Label,storage.Path));
|
||
|
}
|
||
|
svrs=TYTDCommon.GetServers();
|
||
|
foreach(var svr in svrs)
|
||
|
{
|
||
|
items.Add(new ServerItem(svrs,svr.Key,svr.Value));
|
||
|
}
|
||
|
listView.Adapter = new ArrayAdapter<string>(this,Android.Resource.Layout.SimpleListItem1,items.GetNames());
|
||
|
|
||
|
}
|
||
|
}
|
||
|
public override bool OnSupportNavigateUp()
|
||
|
{
|
||
|
OnBackPressed();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
public override bool OnCreateOptionsMenu(IMenu? menu)
|
||
|
{
|
||
|
MenuInflater.Inflate(Resource.Menu.menu_servers, menu);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public override bool OnOptionsItemSelected(IMenuItem item)
|
||
|
{
|
||
|
int id = item.ItemId;
|
||
|
|
||
|
|
||
|
return base.OnOptionsItemSelected(item);
|
||
|
}
|
||
|
|
||
|
private void FabOnClick(object? sender, EventArgs eventArgs)
|
||
|
{
|
||
|
var builder=new AndroidX.AppCompat.App.AlertDialog.Builder(this);
|
||
|
builder.SetView(Resource.Layout.edit_server);
|
||
|
EditText? name =null;
|
||
|
EditText? url=null;
|
||
|
Button? sftp=null;
|
||
|
AndroidX.AppCompat.App.AlertDialog? alert=null;
|
||
|
builder.SetTitle(Resource.String.add_server_text);
|
||
|
builder.SetNegativeButton(Resource.String.cancel_text,(sender,e)=>{
|
||
|
if(alert != null) alert.Dismiss();
|
||
|
});
|
||
|
builder.SetPositiveButton(Resource.String.ok_text,(sender,e)=>{
|
||
|
if(svrs != null&&name!= null && url !=null && !string.IsNullOrWhiteSpace(name.Text) && !string.IsNullOrWhiteSpace(url.Text)){
|
||
|
svrs.AddUnique(name.Text,url.Text);
|
||
|
TYTDCommon.SaveServers(svrs);
|
||
|
UpdateLV();
|
||
|
}
|
||
|
if(alert != null) alert.Dismiss();
|
||
|
});
|
||
|
|
||
|
|
||
|
alert= builder.Show();
|
||
|
if(alert != null)
|
||
|
{
|
||
|
//this.GetExternalFilesDirs(null)
|
||
|
name=alert.FindViewById<EditText>(Resource.Id.server_name_field);
|
||
|
url = alert.FindViewById<EditText>(Resource.Id.server_uri_field);
|
||
|
sftp = alert.FindViewById<Button>(Resource.Id.server_ssh_button);
|
||
|
if(sftp != null)
|
||
|
{
|
||
|
sftp.Click += (sender,e)=>{
|
||
|
if(url != null)
|
||
|
{
|
||
|
SftpEditor(url);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void SftpEditor(EditText uriText)
|
||
|
{
|
||
|
Uri? uri;
|
||
|
string ipOrFqdn="";
|
||
|
string port="";
|
||
|
string username="";
|
||
|
string password="";
|
||
|
string path="";
|
||
|
if(Uri.TryCreate(uriText.Text,UriKind.Absolute,out uri) && uri.Scheme == "sftp")
|
||
|
{
|
||
|
ipOrFqdn = uri.Host;
|
||
|
port = uri.Port.ToString();
|
||
|
|
||
|
string userInfo=uri.UserInfo;
|
||
|
|
||
|
if(!string.IsNullOrWhiteSpace(userInfo))
|
||
|
{
|
||
|
string[] userInfoSplit = userInfo.Split(':',2,StringSplitOptions.RemoveEmptyEntries);
|
||
|
if(userInfoSplit.Length == 2)
|
||
|
{
|
||
|
//username:password
|
||
|
username = userInfoSplit[0];
|
||
|
password = userInfoSplit[1];
|
||
|
}else if(userInfoSplit.Length == 1)
|
||
|
{
|
||
|
//username only
|
||
|
username = userInfoSplit[0];
|
||
|
}
|
||
|
}
|
||
|
path=uri.PathAndQuery;
|
||
|
|
||
|
}
|
||
|
EditText? fqdnTb=null;
|
||
|
EditText? portTb=null;
|
||
|
EditText? usernameTb=null;
|
||
|
EditText? passwordTb=null;
|
||
|
EditText? pathTb=null;
|
||
|
AndroidX.AppCompat.App.AlertDialog? alert=null;
|
||
|
var builder=new AndroidX.AppCompat.App.AlertDialog.Builder(this);
|
||
|
builder.SetTitle(Resource.String.edit_ssh_text);
|
||
|
builder.SetView(Resource.Layout.edit_ssh_server);
|
||
|
builder.SetPositiveButton(Resource.String.ok_text,(sender,e)=>{
|
||
|
if(fqdnTb != null)
|
||
|
{
|
||
|
ipOrFqdn=fqdnTb.Text.GetValOrDefault();
|
||
|
}
|
||
|
if(portTb != null)
|
||
|
{
|
||
|
ushort portN;
|
||
|
if(ushort.TryParse(portTb.Text,out portN))
|
||
|
{
|
||
|
port=portN.ToString();
|
||
|
}else{
|
||
|
port="22";
|
||
|
}
|
||
|
}
|
||
|
if(usernameTb != null)
|
||
|
{
|
||
|
username=usernameTb.Text.GetValOrDefault();
|
||
|
}
|
||
|
if(passwordTb != null)
|
||
|
{
|
||
|
password = passwordTb.Text.GetValOrDefault();
|
||
|
}
|
||
|
if(pathTb != null)
|
||
|
{
|
||
|
path = pathTb.Text.GetValOrDefault();
|
||
|
}
|
||
|
string userInfoStr="";
|
||
|
bool hasUserName = !string.IsNullOrWhiteSpace(username);
|
||
|
bool hasPassword = !string.IsNullOrWhiteSpace(password);
|
||
|
if(hasUserName || hasPassword)
|
||
|
{
|
||
|
if(hasUserName && !hasPassword)
|
||
|
{
|
||
|
userInfoStr = $"{username}@";
|
||
|
}
|
||
|
else if(hasPassword && !hasUserName)
|
||
|
{
|
||
|
userInfoStr = $"root:{password}@";
|
||
|
}else{
|
||
|
userInfoStr = $"{username}:{password}@";
|
||
|
}
|
||
|
}
|
||
|
if(string.IsNullOrWhiteSpace(path)) path="/";
|
||
|
string url = $"sftp://{userInfoStr}{ipOrFqdn}:{port}{path}";
|
||
|
uriText.Text=url;
|
||
|
if(alert!= null) alert.Dismiss();
|
||
|
});
|
||
|
builder.SetNegativeButton(Resource.String.cancel_text,(sender,e)=>{
|
||
|
if(alert != null) alert.Dismiss();
|
||
|
});
|
||
|
alert=builder.Show();
|
||
|
if(alert != null){
|
||
|
fqdnTb=alert.FindViewById<EditText>(Resource.Id.ssh_uri);
|
||
|
if(fqdnTb != null)
|
||
|
fqdnTb.Text = ipOrFqdn;
|
||
|
|
||
|
portTb=alert.FindViewById<EditText>(Resource.Id.ssh_port);
|
||
|
if(portTb != null)
|
||
|
portTb.Text = port;
|
||
|
|
||
|
usernameTb=alert.FindViewById<EditText>(Resource.Id.ssh_username);
|
||
|
if(usernameTb!= null)
|
||
|
usernameTb.Text=username;
|
||
|
|
||
|
passwordTb = alert.FindViewById<EditText>(Resource.Id.ssh_password);
|
||
|
if(passwordTb !=null)
|
||
|
passwordTb.Text=password;
|
||
|
|
||
|
pathTb = alert.FindViewById<EditText>(Resource.Id.ssh_path);
|
||
|
if(pathTb != null)
|
||
|
pathTb.Text = path;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
|
||
|
{
|
||
|
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||
|
|
||
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||
|
}
|
||
|
}
|
||
|
}
|