-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from LittleBigRefresh/android
MVP Android support
- Loading branch information
Showing
38 changed files
with
481 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
10 changes: 10 additions & 0 deletions
10
.idea/.idea.Refresher/.idea/libraries/sentry_android_core_7_8_0.xml
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
10 changes: 10 additions & 0 deletions
10
.idea/.idea.Refresher/.idea/libraries/sentry_android_ndk_7_8_0.xml
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:label="@string/app_name" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:theme="@style/AppTheme"> | ||
</application> | ||
<uses-permission android:name="android.permission.INTERNET" /> | ||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using Android.OS; | ||
using Android.Util; | ||
using NotEnoughLogs; | ||
using NotEnoughLogs.Sinks; | ||
|
||
namespace Refresher.AndroidApp.Logging; | ||
|
||
public class AndroidSink : ILoggerSink | ||
{ | ||
private readonly Handler _handler = new(Looper.MainLooper!); | ||
|
||
public void Log(LogLevel level, ReadOnlySpan<char> category, ReadOnlySpan<char> content) | ||
{ | ||
LogPriority priority = level switch | ||
{ | ||
LogLevel.Critical => LogPriority.Error, | ||
LogLevel.Error => LogPriority.Error, | ||
LogLevel.Warning => LogPriority.Warn, | ||
LogLevel.Info => LogPriority.Info, | ||
LogLevel.Debug => LogPriority.Debug, | ||
LogLevel.Trace => LogPriority.Verbose, | ||
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null), | ||
}; | ||
|
||
Android.Util.Log.WriteLine(priority, "Refresher." + category.ToString(), content.ToString()); | ||
string categoryStr = category.ToString(); | ||
string contentStr = content.ToString(); | ||
this._handler.Post(() => | ||
{ | ||
if (priority == LogPriority.Error) | ||
{ | ||
new AlertDialog.Builder(PipelineActivity.Instance) | ||
.SetTitle($"{categoryStr} Error")? | ||
.SetMessage(contentStr)? | ||
.SetNeutralButton("OK", (_, _) => {})? | ||
.Show(); | ||
} | ||
else if(priority == LogPriority.Warn) | ||
{ | ||
Toast.MakeText(PipelineActivity.Instance, contentStr, ToastLength.Short)?.Show(); | ||
} | ||
}); | ||
} | ||
|
||
public void Log(LogLevel level, ReadOnlySpan<char> category, ReadOnlySpan<char> format, params object[] args) | ||
{ | ||
this.Log(level, category, string.Format(format.ToString(), args)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using _Microsoft.Android.Resource.Designer; | ||
using Android.Content; | ||
using Refresher.AndroidApp.Logging; | ||
using Refresher.Core; | ||
using Refresher.Core.Logging; | ||
using Refresher.Core.Pipelines; | ||
using SCEToolSharp; | ||
|
||
using ConditionalAttribute = System.Diagnostics.ConditionalAttribute; | ||
|
||
namespace Refresher.AndroidApp; | ||
|
||
[Activity(Label = "@string/app_name", MainLauncher = true)] | ||
public class MainActivity : RefresherActivity | ||
{ | ||
protected override void OnCreate(Bundle? savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
|
||
// Set our view from the "main" layout resource | ||
this.SetContentView(ResourceConstant.Layout.activity_main); | ||
|
||
LinearLayout? mainContent = this.FindViewById<LinearLayout>(ResourceConstant.Id.MainContent); | ||
if (mainContent == null) | ||
throw new Exception("Main content not found"); | ||
|
||
this.AddButtonForPipeline<PS3PatchPipeline>(mainContent); | ||
#if DEBUG | ||
this.AddButtonForPipeline<ExamplePipeline>(mainContent); | ||
#endif | ||
this.AddSceToolSharpTestButton(mainContent); | ||
} | ||
|
||
private void AddButtonForPipeline<TPipeline>(LinearLayout layout) where TPipeline : Pipeline | ||
{ | ||
Button button = new(this); | ||
button.Text = typeof(TPipeline).Name; | ||
|
||
button.Click += (_, _) => | ||
{ | ||
Intent intent = new(this, typeof(PipelineActivity)); | ||
intent.PutExtra("PipelineType", typeof(TPipeline).FullName); | ||
this.StartActivity(intent); | ||
}; | ||
|
||
layout.AddView(button); | ||
} | ||
|
||
[Conditional("DEBUG")] | ||
private void AddSceToolSharpTestButton(LinearLayout layout) | ||
{ | ||
Button button = new(this); | ||
button.Text = "DEBUG: Test LibSceToolSharp"; | ||
|
||
button.Click += (_, _) => | ||
{ | ||
string initReturn; | ||
|
||
try | ||
{ | ||
initReturn = LibSceToolSharp.Init().ToString(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
initReturn = ex.ToString(); | ||
} | ||
|
||
new AlertDialog.Builder(this) | ||
.SetTitle("LibSceToolSharp.Init() returns")? | ||
.SetMessage(initReturn)? | ||
.SetPositiveButton("Hell yeah", (_, _) => {})? | ||
.SetNegativeButton("FUCK", (_, _) => {})? | ||
.Show(); | ||
}; | ||
|
||
layout.AddView(button); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
using _Microsoft.Android.Resource.Designer; | ||
using Android.OS; | ||
using Android.Text; | ||
using Android.Views; | ||
using Refresher.Core; | ||
using Refresher.Core.Logging; | ||
using Refresher.Core.Pipelines; | ||
using static Android.Views.ViewGroup.LayoutParams; | ||
|
||
namespace Refresher.AndroidApp; | ||
|
||
[Activity] | ||
public class PipelineActivity : RefresherActivity | ||
{ | ||
internal static PipelineActivity Instance { get; private set; } | ||
|
||
private Pipeline? _pipeline; | ||
private CancellationTokenSource? _cts; | ||
|
||
private TextView _pipelineState = null!; | ||
private LinearLayout _pipelineInputs = null!; | ||
private ScrollView _logScroll = null!; | ||
private TextView _log = null!; | ||
private Button _button = null!; | ||
private ProgressBar _progressBar = null!; | ||
private ProgressBar _currentProgressBar = null!; | ||
|
||
private readonly Handler _handler = new(Looper.MainLooper!); | ||
|
||
public PipelineActivity() | ||
{ | ||
Instance = this; | ||
} | ||
|
||
protected override void OnCreate(Bundle? savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
this.SetContentView(ResourceConstant.Layout.activity_pipeline); | ||
|
||
this._button = this.FindViewById<Button>(ResourceConstant.Id.ExecutePipelineButton)!; | ||
this._button.Click += this.ExecutePipeline; | ||
|
||
this._pipelineState = this.FindViewById<TextView>(ResourceConstant.Id.PipelineState)!; | ||
|
||
this._progressBar = this.FindViewById<ProgressBar>(ResourceConstant.Id.ProgressBar)!; | ||
this._currentProgressBar = this.FindViewById<ProgressBar>(ResourceConstant.Id.CurrentProgressBar)!; | ||
|
||
this._logScroll = this.FindViewById<ScrollView>(ResourceConstant.Id.GlobalLogScroll)!; | ||
this._log = this.FindViewById<TextView>(ResourceConstant.Id.GlobalLog)!; | ||
State.Log += this.OnLog; | ||
|
||
this._pipelineInputs = this.FindViewById<LinearLayout>(ResourceConstant.Id.PipelineInputs)!; | ||
|
||
this.InitializePipeline(); | ||
this.UpdateFormStateLoop(); | ||
} | ||
|
||
private void InitializePipeline() | ||
{ | ||
this._cts = new CancellationTokenSource(); | ||
|
||
string? pipelineTypeName = this.Intent?.GetStringExtra("PipelineType"); | ||
if(pipelineTypeName == null) | ||
throw new Exception("Pipeline type not specified"); | ||
|
||
Type? pipelineType = typeof(Pipeline).Assembly.GetType(pipelineTypeName); | ||
if(pipelineType == null) | ||
throw new Exception("Pipeline was not found"); | ||
|
||
Pipeline pipeline = (Pipeline)Activator.CreateInstance(pipelineType)!; | ||
pipeline.Initialize(); | ||
this._pipeline = pipeline; | ||
|
||
if (this.ActionBar != null) | ||
{ | ||
this.ActionBar.Subtitle = pipeline.Name; | ||
} | ||
else | ||
{ | ||
this.Title = "Refresher - " + pipeline.Name; | ||
} | ||
|
||
this._log.Text = string.Empty; | ||
|
||
ViewGroup.LayoutParams layoutParams = new(MatchParent, WrapContent); | ||
foreach (StepInput input in pipeline.RequiredInputs) | ||
{ | ||
EditText view = new(this); | ||
view.Hint = input.Name; | ||
view.Tag = input.Id; | ||
view.LayoutParameters = layoutParams; | ||
view.InputType = InputTypes.ClassText | InputTypes.TextVariationNormal; | ||
|
||
this._pipelineInputs.AddView(view); | ||
} | ||
} | ||
|
||
private void ExecutePipeline(object? sender, EventArgs e) | ||
{ | ||
if (this._pipeline == null) | ||
return; | ||
|
||
if (this._pipeline.State == PipelineState.Running) | ||
{ | ||
this._cts?.Cancel(); | ||
return; | ||
} | ||
|
||
if (this._pipeline.State is PipelineState.Cancelled or PipelineState.Error or PipelineState.Finished) | ||
{ | ||
this._pipeline.Reset(); | ||
} | ||
|
||
this.UpdateFormState(); | ||
|
||
int inputCount = this._pipelineInputs.ChildCount; | ||
for (int i = 0; i < inputCount; i++) | ||
{ | ||
EditText child = (EditText)this._pipelineInputs.GetChildAt(i)!; | ||
string id = (string)child.Tag!; | ||
string value = child.Text!; | ||
|
||
this._pipeline.Inputs.Add(id, value); | ||
} | ||
|
||
// State.Logger.LogInfo(LogType.Pipeline, "Starting pipeline task..."); | ||
Task.Run(async () => | ||
{ | ||
try | ||
{ | ||
State.Logger.LogInfo(LogType.Pipeline, "Executing Pipeline..."); | ||
await this._pipeline.ExecuteAsync(this._cts?.Token ?? default); | ||
this.UpdateFormState(); | ||
} | ||
catch (Exception ex) | ||
{ | ||
State.Logger.LogError(LogType.Pipeline, $"Error while running pipeline {this._pipeline.Name}: {ex.Message}"); | ||
} | ||
}, this._cts?.Token ?? default); | ||
} | ||
|
||
private void UpdateFormState() | ||
{ | ||
this._pipelineState.Text = this._pipeline?.State.ToString(); | ||
// this._pipelineState.Text = DateTimeOffset.Now.Ticks.ToString(); | ||
|
||
this._progressBar.Progress = (int)((this._pipeline?.Progress ?? 0) * 100); | ||
this._currentProgressBar.Progress = (int)((this._pipeline?.CurrentProgress ?? 0) * 100); | ||
} | ||
|
||
private void UpdateFormStateLoop() | ||
{ | ||
this.UpdateFormState(); | ||
this._handler.PostDelayed(this.UpdateFormStateLoop, this._pipeline?.State == PipelineState.Running ? 16 : 1000); | ||
} | ||
|
||
private void OnLog(RefresherLog log) | ||
{ | ||
this._handler.Post(() => | ||
{ | ||
this._log.Text += $"[{log.Level}] [{log.Category}] {log.Content}\n"; | ||
|
||
this._logScroll.Post(() => | ||
{ | ||
this._logScroll.FullScroll(FocusSearchDirection.Down); | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<TargetFramework>net8.0-android</TargetFramework> | ||
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion> | ||
<OutputType>Exe</OutputType> | ||
<Nullable>enable</Nullable> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<ApplicationId>com.littlebigrefresh.refresher</ApplicationId> | ||
<ApplicationVersion>1</ApplicationVersion> | ||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\Refresher.Core\Refresher.Core.csproj" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Refresher.AndroidApp.Logging; | ||
using Refresher.Core; | ||
using Refresher.Core.Logging; | ||
|
||
namespace Refresher.AndroidApp; | ||
|
||
public abstract class RefresherActivity : Activity | ||
{ | ||
protected override void OnCreate(Bundle? savedInstanceState) | ||
{ | ||
base.OnCreate(savedInstanceState); | ||
State.InitializeLogger([new AndroidSink(), new EventSink(), new SentryBreadcrumbSink()]); | ||
} | ||
} |
Oops, something went wrong.