using System.Net.Http.Json; using System.Text.Json.Serialization; using Android.Content; using Android.Runtime; using Android.Views; using AndroidX.AppCompat.App; using AndroidX.RecyclerView.Widget; using AndroidX.SwipeRefreshLayout.Widget; using Google.Android.Material.Color; using Google.Android.Material.FloatingActionButton; using Google.Android.Material.Snackbar; using Google.Android.Material.TextField; namespace SimpleNotes; [Activity(Label = "@string/app_name", MainLauncher = true,Theme ="@style/Theme.SimpleNotes")] public class MainActivity : AppCompatActivity { public static (string Endpoint,string Username,string Password) GetPrefs(Context ctx) { var config=ctx.GetSharedPreferences("config",Android.Content.FileCreationMode.Private); if(config != null) { var endpoint = config.GetString("endpoint","") ?? ""; var username = config.GetString("username","") ?? ""; var password = config.GetString("password","") ?? ""; return (endpoint.TrimEnd('/'),username,password); } return ("","",""); } public static readonly HttpClient Client =new HttpClient(); public override bool OnCreateOptionsMenu(IMenu? menu) { MenuInflater.Inflate(Resource.Menu.menu_main,menu); return true; } public override bool OnOptionsItemSelected(IMenuItem item) { int id=item.ItemId; if(id == Resource.Id.action_settings) { var intent = new Intent(this,typeof(SettingActivity)); StartActivityForResult(intent,0); } return base.OnOptionsItemSelected(item); } Func? fItems; protected override void OnCreate(Bundle? savedInstanceState) { base.OnCreate(savedInstanceState); DynamicColors.ApplyToActivityIfAvailable(this); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); SetSupportActionBar(FindViewById(Resource.Id.toolbar)); FloatingActionButton? fab = FindViewById(Resource.Id.fab); SwipeRefreshLayout? swipeRefreshLayout = FindViewById(Resource.Id.swiperefresh); RecyclerView? recyclerView = FindViewById(Resource.Id.recyclerView); if(fab == null || swipeRefreshLayout == null || recyclerView == null) return; recyclerView.SetLayoutManager(new LinearLayoutManager(this)); recyclerView.SetAdapter(new NoteAdapter(new List())); fab.Click += (sender,e)=>{ var intent = new Intent(this,typeof(NoteActivity)); StartActivityForResult(intent,0); }; SemaphoreSlim slim=new SemaphoreSlim(1,1); fItems = FetchItems; async Task FetchItems() { await slim.WaitAsync(); Console.WriteLine("ready to fetch"); var (endpoint,username,password) = GetPrefs(this); if(!string.IsNullOrWhiteSpace(endpoint) && !string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password)) { var request = new HttpRequestMessage(HttpMethod.Get,$"{endpoint}/api/note"); request.Headers.Add("Authorization",$"Basic {Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{username}:{password}"))}"); var response=await MainActivity.Client.SendAsync(request); if(response.IsSuccessStatusCode) { Console.WriteLine("Success status code"); var notes = await response.Content.ReadFromJsonAsync<_NotesResponse>(); Console.WriteLine($"{notes == null} {notes?.Success ?? false}"); if(notes != null && notes.Success) { this.RunOnUiThread(()=>{ var adapter = new NoteAdapter(notes.Notes); adapter.LaunchNote += LaunchNote; adapter.DeleteNote += DeleteNote; recyclerView.SetAdapter(adapter); }); void LaunchNote(object? sender,long e) { Intent intent=new Intent(this,typeof(NoteActivity)); intent.PutExtra("note_id",e); StartActivityForResult(intent,0); } void DeleteNote(object? sender,long e){ var alert=new AndroidX.AppCompat.App.AlertDialog.Builder(this); alert.SetTitle("Delete Note?"); alert.SetPositiveButton("Yes",async(sender2,e2)=>{ var request = new HttpRequestMessage(HttpMethod.Delete,$"{endpoint}/api/note?id={e}"); request.Headers.Add("Authorization",$"Basic {Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes($"{username}:{password}"))}"); var response=await MainActivity.Client.SendAsync(request); if(response.IsSuccessStatusCode) { var data = await response.Content.ReadFromJsonAsync<_SuccessResp>(); if(data == null || !data.Success) return; Note? sen = sender as Note; if(sen != null) notes.Notes.Remove(sen); this.RunOnUiThread(()=>{ var adapter2 = new NoteAdapter(notes.Notes); adapter2.DeleteNote += DeleteNote; adapter2.LaunchNote += LaunchNote; recyclerView.SetAdapter(adapter2); }); } }); alert.SetNegativeButton("No",(sender2,e2)=>{}); alert.Create().Show(); } } } } slim.Release(); } swipeRefreshLayout.Refresh += async(sender,e)=>{ await FetchItems(); this.RunOnUiThread(()=>{ swipeRefreshLayout.Refreshing=false; }); }; Task.Run(FetchItems).Wait(0); } protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent? data) { base.OnActivityResult(requestCode, resultCode, data); if(fItems != null) Task.Run(fItems).Wait(0); } } internal class NoteAdapter : RecyclerView.Adapter { private Note[] notes; public event EventHandler? LaunchNote; public event EventHandler? DeleteNote; public NoteAdapter(List notes) { this.notes = notes.ToArray(); } public void InvokeDeleteNote(Note n,long note) { DeleteNote?.Invoke(n,note); } public void InvokeLaunchNode(Note n,long note) { LaunchNote?.Invoke(n,note); } public override int ItemCount => notes.Length; public class NoteViewHolder : RecyclerView.ViewHolder { public Note Item {set { item = value; if(tv != null) { tv.Text = item.Title; } }} ImageButton? imageButton; TextView? tv; Note item=new Note(); public NoteViewHolder(View itemView,NoteAdapter adapter) : base(itemView) { tv=itemView.FindViewById(Resource.Id.rvtextView); imageButton = itemView.FindViewById(Resource.Id.rvimagebutton); if(imageButton != null) { imageButton.Click += (sender,e)=>{ adapter.InvokeDeleteNote(this.item,this.item.Id); }; } itemView.Click += (sender,e)=>{ adapter.InvokeLaunchNode(this.item,this.item.Id); }; } } public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { var noteHolder=holder as NoteViewHolder; if(noteHolder != null) { noteHolder.Item = notes[position]; } } public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { View? itemView = LayoutInflater.From(parent.Context)?.Inflate(Resource.Layout.note_view, parent, false); if(itemView == null) throw new Exception(); return new NoteViewHolder(itemView,this); } } public class _SuccessResp { [JsonPropertyName("success")] public bool Success {get;set;} } public class _NotesResponse { [JsonPropertyName("success")] public bool Success {get;set;} [JsonPropertyName("notes")] public List Notes {get;set;}=new List(); }