Commit:
4ed961bParent:
e7bada9Add read-only note view screen
Tapping a note in the list now opens its full raw markdown content (monospace, unrendered, selectable), showing the cached preview instantly while the full content loads over Graph. Editing and note creation remain out of scope for this milestone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
src/Activities/NoteListActivity.cs
+11
-1
diff --git a/src/Activities/NoteListActivity.cs b/src/Activities/NoteListActivity.cs
index 9f8e794..5edfb10 100644
@@ -1,3 +1,4 @@
using Android.Content;
using Android.Views;
using AndroidX.RecyclerView.Widget;
using NotesApp.Adapters;
@@ -24,7 +25,7 @@ public class NoteListActivity : Activity
_recyclerView = FindViewById<RecyclerView>(Resource.Id.note_recycler_view)!;
_recyclerView.SetLayoutManager(new LinearLayoutManager(this));
_adapter = new NoteListAdapter();
_adapter = new NoteListAdapter(OnNoteTapped);
_recyclerView.SetAdapter(_adapter);
_progressBar = FindViewById<ProgressBar>(Resource.Id.note_refresh_progress)!;
@@ -86,6 +87,15 @@ public class NoteListActivity : Activity
}
}
private void OnNoteTapped(NoteCacheEntry entry)
{
var intent = new Intent(this, typeof(NoteViewActivity));
intent.PutExtra(NoteViewActivity.ExtraNoteId, entry.Id);
intent.PutExtra(NoteViewActivity.ExtraNoteName, entry.Name);
intent.PutExtra(NoteViewActivity.ExtraNotePreview, entry.Preview);
StartActivity(intent);
}
private void BindItems(List<NoteCacheEntry> entries)
{
_adapter.SetItems(entries);
src/Activities/NoteViewActivity.cs
+52
-0
diff --git a/src/Activities/NoteViewActivity.cs b/src/Activities/NoteViewActivity.cs
new file mode 100644
index 0000000..ed34290
@@ -0,0 +1,52 @@
using Android.Views;
using NotesApp.Graph;
namespace NotesApp.Activities;
[Activity(Label = "@string/app_name", Exported = false)]
public class NoteViewActivity : Activity
{
public const string ExtraNoteId = "note_id";
public const string ExtraNoteName = "note_name";
public const string ExtraNotePreview = "note_preview";
private TextView _contentView = null!;
private ProgressBar _progressBar = null!;
private GraphService _graphService = null!;
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_note_view);
var noteId = Intent?.GetStringExtra(ExtraNoteId);
Title = Intent?.GetStringExtra(ExtraNoteName) ?? GetString(Resource.String.app_name);
_contentView = FindViewById<TextView>(Resource.Id.note_view_content)!;
_progressBar = FindViewById<ProgressBar>(Resource.Id.note_view_progress)!;
_contentView.Text = Intent?.GetStringExtra(ExtraNotePreview) ?? "";
_graphService = new GraphService(this);
if (noteId != null)
_ = LoadContentAsync(noteId);
}
private async Task LoadContentAsync(string noteId)
{
_progressBar.Visibility = ViewStates.Visible;
try
{
_contentView.Text = await _graphService.GetContentAsync(noteId);
}
catch (Exception ex)
{
Toast.MakeText(this, ex.Message, ToastLength.Long)?.Show();
// Leave the preview text in place as a partial fallback rather than blanking it.
}
finally
{
_progressBar.Visibility = ViewStates.Gone;
}
}
}
src/Adapters/NoteListAdapter.cs
+12
-3
diff --git a/src/Adapters/NoteListAdapter.cs b/src/Adapters/NoteListAdapter.cs
index 295a733..7809e7b 100644
@@ -4,7 +4,7 @@ using NotesApp.Models;
namespace NotesApp.Adapters;
public class NoteListAdapter : RecyclerView.Adapter
public class NoteListAdapter(Action<NoteCacheEntry> onNoteTapped) : RecyclerView.Adapter
{
private List<NoteCacheEntry> _items = [];
@@ -21,7 +21,7 @@ public class NoteListAdapter : RecyclerView.Adapter
var view = LayoutInflater
.From(parent.Context)!
.Inflate(Resource.Layout.row_note, parent, false)!;
return new NoteViewHolder(view);
return new NoteViewHolder(view, onNoteTapped);
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) =>
@@ -31,16 +31,25 @@ public class NoteListAdapter : RecyclerView.Adapter
{
private readonly TextView _nameView;
private readonly TextView _previewView;
private readonly Action<NoteCacheEntry> _onNoteTapped;
private NoteCacheEntry? _item;
public NoteViewHolder(View itemView)
public NoteViewHolder(View itemView, Action<NoteCacheEntry> onNoteTapped)
: base(itemView)
{
_nameView = itemView.FindViewById<TextView>(Resource.Id.note_name)!;
_previewView = itemView.FindViewById<TextView>(Resource.Id.note_preview)!;
_onNoteTapped = onNoteTapped;
itemView.Click += (_, _) =>
{
if (_item != null)
_onNoteTapped(_item);
};
}
public void Bind(NoteCacheEntry entry)
{
_item = entry;
_nameView.Text = entry.Name;
_previewView.Text = entry.Preview;
}
src/AndroidManifest.xml
+1
-0
diff --git a/src/AndroidManifest.xml b/src/AndroidManifest.xml
index 1409521..cc6f90e 100644
@@ -10,6 +10,7 @@
<activity android:name=".Activities.SettingsActivity" android:exported="false" />
<activity android:name=".Activities.FolderPickerActivity" android:exported="false" />
<activity android:name=".Activities.NoteListActivity" android:exported="false" />
<activity android:name=".Activities.NoteViewActivity" android:exported="false" />
<activity android:name="microsoft.identity.client.BrowserTabActivity" android:exported="true">
<intent-filter>
src/Graph/GraphService.cs
+15
-0
diff --git a/src/Graph/GraphService.cs b/src/Graph/GraphService.cs
index f867cd8..099748e 100644
@@ -190,4 +190,19 @@ public class GraphService
const int maxLength = 300;
return trimmed.Length > maxLength ? trimmed[..maxLength] : trimmed;
}
public async Task<string> GetContentAsync(string itemId, CancellationToken ct = default)
{
var driveId = await GetDriveIdAsync(ct);
var stream = await _client
.Drives[driveId]
.Items[itemId]
.Content.GetAsync(cancellationToken: ct);
if (stream is null)
return "";
using var reader = new StreamReader(stream, Encoding.UTF8);
return await reader.ReadToEndAsync(ct);
}
}
src/Resources/layout/activity_note_view.xml
+24
-0
diff --git a/src/Resources/layout/activity_note_view.xml b/src/Resources/layout/activity_note_view.xml
new file mode 100644
index 0000000..2be7e00
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView android:layout_width="match_parent" android:layout_height="match_parent">
<TextView
android:id="@+id/note_view_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:fontFamily="monospace"
android:textIsSelectable="true"
/>
</ScrollView>
<ProgressBar
android:id="@+id/note_view_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
/>
</FrameLayout>