Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AppCenterをセットアップ #91

Merged
merged 48 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d675a4c
とりあえずAppCenterをセットアップ
TetsuOtter Oct 23, 2023
8c22ecf
AppCenterにdSYMをアップロードするようにした
TetsuOtter Oct 23, 2023
049a6ed
テスト用のクラッシュボタン、およびクラッシュログ送信ポリシーを設定
TetsuOtter Oct 23, 2023
bd9d707
class nameを付け忘れていたので追加
TetsuOtter Oct 23, 2023
d51d485
dSYMのUUIDのみを出力するようにした
TetsuOtter Oct 23, 2023
daf9642
きちんとdSYMをAppCenterにアップロードできるようにした
TetsuOtter Oct 23, 2023
97f6d99
TestCrashがReleaseだと効かないので、別のCrashを用意した
TetsuOtter Oct 23, 2023
e360dc2
Create PrivacyPolicy.md
TetsuOtter Oct 25, 2023
fddbc4d
Create Utils.common.cs
TetsuOtter Oct 25, 2023
a7e7205
Create OpenUrlCommand.cs
TetsuOtter Oct 25, 2023
a06d65e
MarkdownをRawAssetに含めるようにした
TetsuOtter Oct 25, 2023
d167b8e
Create SimpleMarkdownLabel.cs
TetsuOtter Oct 25, 2023
1480bd8
Create ShowMarkdownPage.cs
TetsuOtter Oct 25, 2023
971096d
PrivacyPolicyを表示するようにした
TetsuOtter Oct 25, 2023
4c4af4c
EasterEggPageをRootPages配下に移動
TetsuOtter Oct 26, 2023
f9fc561
ThirdPartyLicensesをRootPages配下に移動
TetsuOtter Oct 26, 2023
291f001
SelectTrainPageをRootPages配下に移動
TetsuOtter Oct 26, 2023
b867837
AppCenter周りの処理をAppCenterServicesに移動
TetsuOtter Oct 26, 2023
f5c3510
[skip ci] Create AppPreferenceService.cs
TetsuOtter Oct 26, 2023
db6e0e2
Loggerの初期化を別クラスに移動
TetsuOtter Oct 29, 2023
02ba9aa
PreferenceKey文字列の取得を関数化
TetsuOtter Oct 29, 2023
b570445
Set関数も追加
TetsuOtter Oct 29, 2023
ab0e530
Analyticsも有効無効を切り替えられるようにした
TetsuOtter Oct 29, 2023
6082a66
Create IAppCenterSetting.cs
TetsuOtter Oct 29, 2023
6166465
IAppCenterSettingを用いて初期化を行えるようにした
TetsuOtter Oct 29, 2023
5e13f9c
ShowMdPageからSimpleMarkdownViewに移行
TetsuOtter Oct 29, 2023
3782054
有効/無効の選択権について明記
TetsuOtter Oct 29, 2023
c51897b
遅延読み込み機能を実装
TetsuOtter Oct 29, 2023
8015726
LoggerがNullRefで落ちるバグを修正
TetsuOtter Oct 29, 2023
500fcce
Create AppCenterSettingViewModel.cs
TetsuOtter Oct 29, 2023
f07ce8e
AppCenterSettingViewModelもInstanceManagerで管理するようにした
TetsuOtter Oct 29, 2023
c034dca
AppCenterが既に有効な際は、AppShellの起動時点でSetupをかけるようにした
TetsuOtter Oct 29, 2023
6a5a708
[skip ci] AppCenterSettingPageを実装
TetsuOtter Oct 29, 2023
5ff64a8
Binding設定を追加
TetsuOtter Nov 1, 2023
9254f55
設定値を復元できるようボタンを追加
TetsuOtter Nov 1, 2023
43e3dbd
doSaveの判定が逆になっていたので修正
TetsuOtter Nov 1, 2023
474975c
SelectTrainPageへのRouteを設定
TetsuOtter Nov 1, 2023
8176b05
SwitchにOnColorを設定
TetsuOtter Nov 1, 2023
e9d8631
AppCenterSettingPageにもRouteを設定
TetsuOtter Nov 1, 2023
105a32d
Analyticsの追加を忘れてたので修正
TetsuOtter Nov 1, 2023
24cbda5
サービスが初期状態で正常に開始されないバグに対応
TetsuOtter Nov 1, 2023
9ed0b01
ログ出力を追加
TetsuOtter Nov 1, 2023
64434ba
値の保存/反映/ページ移動等の処理を実装
TetsuOtter Nov 1, 2023
d799059
AppCenterが有効化されていない状態では、最初にAppCenterSetting以外を表示できないようにした
TetsuOtter Nov 1, 2023
5885b30
AppCenterのFlyoutItem表示位置を下の方に移動した
TetsuOtter Nov 1, 2023
95da813
アプリ起動ごとにログをアーカイブするようにした
TetsuOtter Nov 2, 2023
544ba5e
ログを共有しない設定を反映するようにした
TetsuOtter Nov 2, 2023
d78dae7
テスト用のクラッシュボタンを削除
TetsuOtter Nov 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/cd-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ env:
AAB_PATH: ./out/dev.t0r.trvis-Signed.aab
APK_PATH: ./out/dev.t0r.trvis-Signed.apk
SDK_VERSION: '8.0.100-preview.4.23260.5'
APP_CENTER_SECRETS_FILE_NAME: ./TRViS/AppCenterSecrets.cs
IOS_DSYM_PATH: ./TRViS/bin/Release/net8.0-ios/ios-arm64
IOS_DSYM_FILENAME: TRViS.app.dSYM

jobs:
generate-bundle-version:
Expand Down Expand Up @@ -205,6 +208,15 @@ jobs:
- name: Print Third Party License Info Files
run: cat ${{ env.THIRD_PARTY_LICENSE_INFO_DIR }}/${{ env.THIRD_PARTY_LICENSE_LIST_NAME }}.json

- name: Add AppCenterSecrets.cs
run: |
echo "namespace TRViS;" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "class AppCenterSecrets" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "{" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo 'public const string IOS = "${{ secrets.APP_CENTER_SECRET_IOS }}";' >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "}" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
ls -l ${{ env.APP_CENTER_SECRETS_FILE_NAME }}

- name: Build
run: >
dotnet publish ${{ env.CSPROJ_PATH }}
Expand All @@ -224,6 +236,15 @@ jobs:
path: ${{ env.IPA_PATH }}
retention-days: 3

- name: tar dSYM
run: cd ${{ env.IOS_DSYM_PATH }} && tar -czf ${{ env.IOS_DSYM_FILENAME }}.tar.gz ${{ env.IOS_DSYM_FILENAME }}
- name: Upload dSYM
uses: actions/upload-artifact@v3
with:
name: dSYM
path: ${{ env.IOS_DSYM_PATH }}/${{ env.IOS_DSYM_FILENAME }}.tar.gz
retention-days: 3

- name: Post 'Import Code Sign Certificates'
if: always() && steps.import-code-sign-cert.conclusion == 'success'
run: /usr/bin/security delete-keychain ${{ steps.gen-keychain-name.outputs.keychain-name }}.keychain
Expand Down Expand Up @@ -275,6 +296,15 @@ jobs:
- name: Print Third Party License Info Files
run: cat ${{ env.THIRD_PARTY_LICENSE_INFO_DIR }}/${{ env.THIRD_PARTY_LICENSE_LIST_NAME }}.json

- name: Add AppCenterSecrets.cs
run: |
echo "namespace TRViS;" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "class AppCenterSecrets" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "{" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo 'public const string ANDROID = "${{ secrets.APP_CENTER_SECRET_ANDROID }}";' >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "}" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
ls -l ${{ env.APP_CENTER_SECRETS_FILE_NAME }}

- name: Build
run: >
dotnet publish ${{ env.CSPROJ_PATH }}
Expand Down Expand Up @@ -382,6 +412,15 @@ jobs:
- name: Print Third Party License Info Files
run: cat ${{ env.THIRD_PARTY_LICENSE_INFO_DIR }}/${{ env.THIRD_PARTY_LICENSE_LIST_NAME }}.json

- name: Add AppCenterSecrets.cs
run: |
echo "namespace TRViS;" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "class AppCenterSecrets" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "{" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo 'public const string MACOS = "${{ secrets.APP_CENTER_SECRET_MACOS }}";' >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
echo "}" >> ${{ env.APP_CENTER_SECRETS_FILE_NAME }}
ls -l ${{ env.APP_CENTER_SECRETS_FILE_NAME }}

- name: Build
run: >
dotnet publish ${{ env.CSPROJ_PATH }}
Expand Down Expand Up @@ -465,6 +504,24 @@ jobs:
--apiKey ${{ secrets.APPSTORECONNECT_API_KEY }}
--apiIssuer ${{ secrets.APPSTORECONNECT_ISSUER_ID }}

- name: Download dSYM
uses: actions/download-artifact@v3
with:
name: dSYM
path: .
- name: extract dSYM
run: tar -xzf ${{ env.IOS_DSYM_FILENAME }}.tar.gz
- name: Check dSYM
run: dwarfdump -u ${{ env.IOS_DSYM_FILENAME }}
- name: Install AppCenter CLI
run: npm install -g appcenter-cli
- name: upload dSYM to AppCenter
run: >
appcenter crashes upload-symbols
--token ${{ secrets.APPCENTER_ACCESS_TOKEN_IOS }}
--app 'Tetsu_Otter/TRViS-iOS-Beta'
--symbol ${{ env.IOS_DSYM_FILENAME }}

publish-mac:
if: |
!failure()
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
TRViS/Resources/Raw/licenses/*
TRViS/AppCenterSecrets.cs

# globs
Makefile.in
Expand Down
4 changes: 4 additions & 0 deletions TRViS/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;

namespace TRViS;

public partial class App : Application
Expand Down
9 changes: 9 additions & 0 deletions TRViS/AppCenterSecrets_cs.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TRViS;

static internal class AppCenterSecrets
{
public const string IOS = "";
public const string ANDROID = "";
public const string WINDOWS = "";
public const string MACOS = "";
}
25 changes: 21 additions & 4 deletions TRViS/AppShell.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TRViS"
xmlns:pages="clr-namespace:TRViS.RootPages"
xmlns:dtac="clr-namespace:TRViS.DTAC"
FlyoutBackgroundColor="{AppThemeBinding Light=#EEE, Dark=#111}">
<Shell.FlyoutIcon>
Expand All @@ -29,16 +30,32 @@
</VerticalStackLayout>
</Shell.FlyoutFooter>

<FlyoutItem Title="Select Train">
<ShellContent ContentTemplate="{DataTemplate local:SelectTrainPage}" />
<FlyoutItem Title="Select Train" Route="{x:Static pages:SelectTrainPage.NameOfThisClass}">
<ShellContent ContentTemplate="{DataTemplate pages:SelectTrainPage}" />
</FlyoutItem>
<FlyoutItem Title="D-TAC">
<ShellContent ContentTemplate="{DataTemplate dtac:ViewHost}" />
</FlyoutItem>
<FlyoutItem Title="Third Party Licenses">
<ShellContent ContentTemplate="{DataTemplate local:ThirdPartyLicenses}" />
<ShellContent ContentTemplate="{DataTemplate pages:ThirdPartyLicenses}" />
</FlyoutItem>
<FlyoutItem Title="Settings">
<ShellContent ContentTemplate="{DataTemplate local:EasterEggPage}" />
<ShellContent ContentTemplate="{DataTemplate pages:EasterEggPage}" />
</FlyoutItem>
<FlyoutItem Title="AppCenter Setting" Route="{x:Static pages:AppCenterSettingPage.NameOfThisClass}">
<ShellContent ContentTemplate="{DataTemplate pages:AppCenterSettingPage}" />
</FlyoutItem>
<FlyoutItem Title="Privacy Policy">
<ShellContent>
<pages:ShowMarkdownPage
FileName="PrivacyPolicy.md"
Title="Privacy Policy"
/>
</ShellContent>
</FlyoutItem>

<MenuItem
Text="Privacy Policy (online)"
Command="{x:Static local:Utils.OpenUrlCommand}"
CommandParameter="https://github.com/TetsuOtter/TRViS/blob/main/docs/md/PrivacyPolicy.md" />
</Shell>
40 changes: 40 additions & 0 deletions TRViS/AppShell.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#if IOS
using System.Runtime.Versioning;
#endif

using TRViS.RootPages;
using TRViS.ViewModels;

namespace TRViS;
Expand All @@ -11,22 +14,59 @@ public partial class AppShell : Shell
static public string AppVersionString
=> $"Version: {AppInfo.Current.VersionString}-{AppInfo.Current.BuildString}";

readonly AppCenterSettingViewModel AppCenterSettingViewModel = InstanceManager.AppCenterSettingViewModel;

public AppShell()
{
logger.Trace("AppShell Creating");
logger.Info("Application Version: {0}", AppVersionString);

EasterEggPageViewModel easterEggPageViewModel = InstanceManager.EasterEggPageViewModel;

logger.Trace("Checking AppCenter Setting");
if (AppCenterSettingViewModel.IsEnabled)
{
logger.Trace("AppCenter Applying...");
AppCenterSettingViewModel.SaveAndApplySettings(false).ConfigureAwait(false);
}

InitializeComponent();

if (AppCenterSettingViewModel.IsEnabled)
{
GoToAsync("//" + nameof(SelectTrainPage)).ConfigureAwait(false);
}
else
{
GoToAsync("//" + nameof(AppCenterSettingPage)).ConfigureAwait(false);
}

AppCenterSettingViewModel.IsEnabledChanged += ApplyFlyoutBhavior;
ApplyFlyoutBhavior(this, false, AppCenterSettingViewModel.IsEnabled);

SetBinding(Shell.BackgroundColorProperty, new Binding() { Source = easterEggPageViewModel, Path = nameof(EasterEggPageViewModel.ShellBackgroundColor) });
SetBinding(Shell.TitleColorProperty, new Binding() { Source = easterEggPageViewModel, Path = nameof(EasterEggPageViewModel.ShellTitleTextColor) });

FlyoutIconImage.SetBinding(FontImageSource.ColorProperty, new Binding() { Source = easterEggPageViewModel, Path = nameof(EasterEggPageViewModel.ShellTitleTextColor) });
logger.Trace("AppShell Created");
}

void ApplyFlyoutBhavior(object? sender, bool oldValue, bool newValue)
{
logger.Trace("{0} -> {1}", oldValue, newValue);
if (newValue == true)
{
FlyoutIcon = FlyoutIconImage;
FlyoutBehavior = FlyoutBehavior.Flyout;
}
else
{
FlyoutIcon = null;
FlyoutBehavior = FlyoutBehavior.Disabled;
FlyoutIsPresented = false;
}
}

public event ValueChangedEventHandler<Thickness>? SafeAreaMarginChanged;
Thickness _SafeAreaMargin;
public Thickness SafeAreaMargin
Expand Down
142 changes: 142 additions & 0 deletions TRViS/Controls/SimpleMarkdownLabel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System.Runtime.CompilerServices;
using System.Text;
using DependencyPropertyGenerator;

namespace TRViS.Controls;

[DependencyProperty<string>("MarkdownFileContent")]
public partial class SimpleMarkdownLabel : Label
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

const int H0_FONT_SIZE = 28;
const int HEADER_FONT_SIZE_STEP = 3;
const double HEADER_LINE_HEIGHT = 1.25;
const int MAX_HEADER_LEVEL = 3;

public SimpleMarkdownLabel()
{
logger.Trace("Creating...");
LineHeight = 1.1;
LineBreakMode = LineBreakMode.WordWrap;
logger.Trace("Created.");
}

partial void OnMarkdownFileContentChanged(string? newValue)
{
if (string.IsNullOrEmpty(newValue))
{
logger.Trace("Text is null or empty.");
if (MainThread.IsMainThread)
FormattedText = null;
else
MainThread.BeginInvokeOnMainThread(() => FormattedText = null);
return;
}

logger.Debug("Text: {0}", newValue);
FormattedString formattedString = new();
SetMarkdownSpanList(newValue, formattedString.Spans);
if (MainThread.IsMainThread)
{
FormattedText = formattedString;
logger.Trace("FormattedText Updated: Count={0}", FormattedText.Spans.Count);
}
else
{
MainThread.BeginInvokeOnMainThread(() => {
FormattedText = formattedString;
logger.Trace("FormattedText Updated: Count={0}", FormattedText.Spans.Count);
});
}
}

static void SetMarkdownSpanList(in string inputText, IList<Span> spanList)
{
string[] inputTextArr = inputText.Split('\n');

static Span FlushToSpan(StringBuilder sb)
{
if (sb.Length == 0)
return new Span();

logger.Trace(">> 0 < sb.Length");
Span span = new()
{
Text = sb.ToString(),
};
sb.Clear();
return span;
}

StringBuilder sb = new();
foreach (string inputTextLine in inputTextArr)
{
bool isHeaderLine = inputTextLine.StartsWith('#');
bool isUrlLine = inputTextLine.StartsWith("https://");

logger.Trace(
"inputTextLine(header:{1}, url:{2}): '{0}'",
inputTextLine,
isHeaderLine,
isUrlLine
);
if (!isHeaderLine && !isUrlLine)
{
logger.Trace("> normal text");
sb.AppendLine(inputTextLine);
continue;
}
if (0 < sb.Length)
spanList.Add(FlushToSpan(sb));
if (isHeaderLine)
{
int headerLevel = 0;
for (int i = 0; i < MAX_HEADER_LEVEL; i++)
{
if (inputTextLine[i] != '#')
break;
headerLevel++;
}
logger.Trace("> headerLevel: '{0}'", headerLevel);

spanList.Add(new Span
{
Text = inputTextLine[headerLevel..].Trim(),
FontSize = H0_FONT_SIZE - headerLevel * HEADER_FONT_SIZE_STEP,
LineHeight = HEADER_LINE_HEIGHT,
});
}
else if (isUrlLine)
{
Span linkSpan = new()
{
Text = inputTextLine + '\n',
FontAttributes = FontAttributes.Italic,
TextDecorations = TextDecorations.Underline,
TextColor = Colors.Aqua,
GestureRecognizers =
{
new TapGestureRecognizer
{
Command = Utils.OpenUrlCommand,
CommandParameter = inputTextLine,
},
},
};
linkSpan.SetAppThemeColor(Span.TextColorProperty, Colors.Blue, Colors.Aqua);
spanList.Add(linkSpan);
}
else
{
logger.Trace("> normal text");
sb.AppendLine(inputTextLine);
}
}

if (0 < sb.Length)
spanList.Add(FlushToSpan(sb));

logger.Debug("spanList.Count: {0}", spanList.Count);
}
}
Loading