Skip to content

Commit

Permalink
Initial Android support
Browse files Browse the repository at this point in the history
(It runs on Android but with very limited feature set)
Refactor project files to support multiple platforms (Desktop and Android)
Move and adjust resource names as Android only supports very limited paths and file names (drawable folder and no "-" allowed)
Improve license generation keep license file readable (auto referenced NuGet packages and dual licenses)
Simplify namespace names for XAML (c for common, v for view ...)
Improved selection of the correct Assembly for reading resources or versions
Split all Pages into Page and Window sources
- Desktop applications will continue working with windows so they continue using the Windows classes
- Android can only work with the new SingleViewNavigationPage which is always present and only switches between the Pages
- The Pages are UserControls only now
- The Windows are used for Desktop applications only and act as a wrapper for the Pages
  • Loading branch information
topeterk committed Dec 9, 2023
1 parent 4ed1ed6 commit 90af803
Show file tree
Hide file tree
Showing 100 changed files with 2,679 additions and 647 deletions.
File renamed without changes
6 changes: 3 additions & 3 deletions Source/Datafiles/Designs/javascript.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//MIT License

//Copyright (c) 2018-2022 Peter Kirmeier
//Copyright (c) 2018-2023 Peter Kirmeier

//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -59,8 +59,8 @@ function IntToStringSigned(i) { if (i == 0) return '0'; else if (i > 0) return '

function ShowCrossOrCheckMark(i) { return '<img src="' + (i > 0 ? 'img_cross.png" height="15px"' : ( i == 0 ? 'img_check.png" height="21px"' : 'img_bar.png" height="21px"')) + '>'; }

function ShowSessionProgress() { return '<img src="icons8-24-64.png" height="21px">'; }
function ShowBestProgress() { return '<img src="icons8-flagpole-64.png" height="21px">'; }
function ShowSessionProgress() { return '<img src="icons8_24_64.png" height="21px">'; }
function ShowBestProgress() { return '<img src="icons8_flagpole_64.png" height="21px">'; }

function OffsetTimerTick() { if (offset_timer_tick_callback != undefined) offset_timer_tick_callback(Date.now() - offset_timer_start); }

Expand Down
25 changes: 25 additions & 0 deletions Source/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project>
<PropertyGroup>
<!-- Consumed by pipeline START -->
<Version>2.0.0.0</Version>
<Product>HitCounterManager</Product>
<!-- Consumed by pipeline END -->
<FileVersion>$(Version)</FileVersion>
<AssemblyVersion>$(Version)</AssemblyVersion>
<AssemblyName>$(Product)</AssemblyName>
<AssemblyTitle>Manages a hit counter</AssemblyTitle>
<NeutralLanguage>en</NeutralLanguage>

<Authors>Peter Kirmeier</Authors>
<Company></Company>
<Copyright>Copyright © $(Authors) $([System.DateTime]::Now.Year)</Copyright>

<Nullable>enable</Nullable>
<!-- The path must be set in this property file as it must be loaded before the -->
<!-- actual project file includes the properties from the dotnet SDK. This is only -->
<!-- needed as Android and Desktop NuGet packages are overwriting each other files -->
<!--<MSBuildProjectExtensionsPath>obj/ext/$(PlatformName)</MSBuildProjectExtensionsPath>-->

<AvaloniaVersion>11.0.5</AvaloniaVersion>
</PropertyGroup>
</Project>
23 changes: 16 additions & 7 deletions Source/GenerateThirdPartyLicense.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,16 @@ EOF
echo Fetching NuGet packages 1>&2

LICENSES=""
NUGET_PACKAGES_DIR=$(dotnet nuget locals -l global-packages | sed -e 's,^[^ ]* ,,')
NUGET_PACKAGES_SPECS=$(dotnet list $(pwd)/HitCounterManager.sln package --include-transitive | grep ">" | sed -e 's,^[^>]*>[ \t]*\([^ ^\t]*\)[ \t]*\([^ ^\t]*\).*,\L\1/\2/\L\1.nuspec,' | xargs)
NUGET_PACKAGES_DIR=$(dotnet nuget locals -l global-packages | sed -e 's,^[^ ]* ,,' -e 's,[ \t/\\]*$,,' -e 's,\\,/,g')
NUGET_PACKAGES_SPECS=$(dotnet list $(pwd)/HitCounterManager.sln package --include-transitive --format json | grep -e '"id":\|"resolvedVersion":' | sed -e 's,.*: "\(.*\)"\,*$,\1,' | xargs -n2 -d'\n' | sed -e 's,\([^ ]*\) \(.*\),\L\1/\2/\L\1.nuspec,')

echo Printing NuGet packages 1>&2

for nuspec in ${NUGET_PACKAGES_SPECS};
for nuspec in ${NUGET_PACKAGES_SPECS}
do
xml="${NUGET_PACKAGES_DIR}/${nuspec}"
# (a) seems to be filler expression to whatever version is available, try detect folder using shell's star autocompletion
xml="$(echo ${NUGET_PACKAGES_DIR}/${nuspec} | sed -e 's,/(a)/,/*/,g')"

id=$(grepkey id "${xml}")
echo ==============================================================================
echo NuGet Package: ${id} $(grepkey version "${xml}")
Expand All @@ -174,13 +176,20 @@ do
if [ ! -z ${GITHUB_RAWURL} ] ; then
lic=${GITHUB_RAWURL}
fi
echo License: ${lic}
LICENSES="${LICENSES} ${lic}"
# Split dual licences
if [ "${lic}" = "https://licenses.nuget.org/MIT%20AND%20Apache-2.0" ] ; then
lic="https://licenses.nuget.org/MIT https://licenses.nuget.org/Apache-2.0"
fi
for lic in ${lic}
do
echo License: ${lic}
LICENSES="${LICENSES} ${lic}"
done
done

echo Printing NuGet licenses 1>&2

for lic in $(echo $LICENSES | xargs -n 1 echo | sort -u) ;
for lic in $(echo $LICENSES | xargs -n 1 echo | sort -u)
do
if [ "${lic}" = "https://licenses.nuget.org/MIT" ] ; then
# MIT is already attached above
Expand Down
32 changes: 32 additions & 0 deletions Source/HitCounterManager.Android/HitCounterManager.Android.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Consumed by pipeline START -->
<TargetFramework>net7.0-android</TargetFramework>
<!-- Consumed by pipeline END -->

<OutputType>Exe</OutputType>
<PackageId>$(Product).Android</PackageId>
<!-- https://docs.microsoft.com/en-us/dotnet/core/rid-catalog -->
<!--<RuntimeIdentifiers>android-arm64</RuntimeIdentifiers>-->
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<ApplicationId>com.peterkirmeier.$(Product)</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
</PropertyGroup>

<ItemGroup>
<AndroidResource Include="Icon.png">
<Link>Resources\drawable\Icon.png</Link>
</AndroidResource>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../$(Product)/$(Product).PCL.csproj" />
<PackageReference Include="Avalonia.Android" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageReference Include="Xamarin.AndroidX.Annotation" Version="1.7.0.3" />
<PackageReference Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SelectedDevice>pixel_5_-_api_34</SelectedDevice>
<SelectedPlatformGroup>Emulator</SelectedPlatformGroup>
<ActiveDebugProfile>Pixel 5 - API 34 (Android 14.0 - API 34)</ActiveDebugProfile>
<DefaultDevice>pixel_5_-_api_34</DefaultDevice>
</PropertyGroup>
</Project>
Binary file added Source/HitCounterManager.Android/Icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions Source/HitCounterManager.Android/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Android.App;
using Android.Content.PM;
using Avalonia;
using Avalonia.Android;
using Avalonia.ReactiveUI;

namespace HitCounterManager.Android;

[Activity(
Label = "HitCounterManager.Android",
Theme = "@style/MyTheme.NoActionBar",
Icon = "@drawable/icon",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
public class MainActivity : AvaloniaMainActivity<App>
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
return base.CustomizeAppBuilder(builder)
.WithInterFont()
.UseReactiveUI();
}
}

/*using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Runtime;
using Avalonia;
using Avalonia.Android;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.ReactiveUI;
using HitCounterManager.Views;
using System.ComponentModel;
namespace HitCounterManager.Android
{
[Activity(
Label = "HitCounterManager.Android",
Theme = "@style/MyTheme.NoActionBar",
Icon = "@drawable/icon",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
public class MainActivity : AvaloniaMainActivity<App>
{
protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
{
return base.CustomizeAppBuilder(builder)
.WithInterFont()
.UseReactiveUI();
}
public override void OnBackPressed()
{
if (App.CurrentApp.ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
// Return to previous page
SingleViewNavigationPage RootPage = (SingleViewNavigationPage)singleViewPlatform.MainView!;
if (1 < RootPage.NavStack.Count)
{
RootPage.PopPage();
return;
}
// Idea of stopping the process by defualt but this will popup the message box
// every time one is about the hit back button. Therefore it is better to keep
// the application running in backgroung.
// We will save the current state during OnStop().
#if EXAMPLE_FORCED_SHUTDOWN
RootPage.MainPage.OnClosing(this, new CancelEventArgs());
App.CurrentApp.AppExitHandler(this, null);
Finish();
#endif
}
base.OnBackPressed();
}
protected override void OnStop()
{
App.CurrentApp.SaveSettings();
base.OnStop();
}
protected override void OnDestroy()
{
App.CurrentApp.AppExitHandler(this, new());
base.OnDestroy();
}
}
}
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="HitCounterManager" android:icon="@drawable/Icon" />
</manifest>
44 changes: 44 additions & 0 deletions Source/HitCounterManager.Android/Resources/AboutResources.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.

For example, a sample Android app that contains a user interface layout (main.axml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:

Resources/
drawable/
icon.png

layout/
main.axml

values/
strings.xml

In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called "R"
(this is an Android convention) that contains the tokens for each one of the resources
included. For example, for the above Resources layout, this is what the R class would expose:

public class R {
public class drawable {
public const int icon = 0x123;
}

public class layout {
public const int main = 0x456;
}

public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}

You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
to reference the layout/main.axml file, or R.strings.first_string to reference the first
string in the dictionary file values/strings.xml.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="128dp"
android:height="128dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group
android:name="wrapper"
android:translateX="21"
android:translateY="21">
<group android:name="group">
<path
android:name="path"
android:pathData="M 74.853 85.823 L 75.368 85.823 C 80.735 85.823 85.144 81.803 85.761 76.602 L 85.836 41.76 C 85.225 18.593 66.254 0 42.939 0 C 19.24 0 0.028 19.212 0.028 42.912 C 0.028 66.357 18.831 85.418 42.18 85.823 L 74.853 85.823 Z"
android:strokeWidth="1"/>
<path
android:name="path_1"
android:pathData="M 43.059 14.614 C 29.551 14.614 18.256 24.082 15.445 36.743 C 18.136 37.498 20.109 39.968 20.109 42.899 C 20.109 45.831 18.136 48.301 15.445 49.055 C 18.256 61.716 29.551 71.184 43.059 71.184 C 47.975 71.184 52.599 69.93 56.628 67.723 L 56.628 70.993 L 71.344 70.993 L 71.344 44.072 C 71.357 43.714 71.344 43.26 71.344 42.899 C 71.344 27.278 58.68 14.614 43.059 14.614 Z M 29.51 42.899 C 29.51 35.416 35.576 29.35 43.059 29.35 C 50.541 29.35 56.607 35.416 56.607 42.899 C 56.607 50.382 50.541 56.448 43.059 56.448 C 35.576 56.448 29.51 50.382 29.51 42.899 Z"
android:strokeWidth="1"
android:fillType="evenOdd"/>
<path
android:name="path_2"
android:pathData="M 18.105 42.88 C 18.105 45.38 16.078 47.407 13.579 47.407 C 11.079 47.407 9.052 45.38 9.052 42.88 C 9.052 40.381 11.079 38.354 13.579 38.354 C 16.078 38.354 18.105 40.381 18.105 42.88 Z"
android:strokeWidth="1"/>
</group>
</group>
</vector>
</aapt:attr>
<target android:name="path">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="fillColor"
android:duration="1000"
android:valueFrom="#00ffffff"
android:valueTo="#161c2d"
android:valueType="colorType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="path_1">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="fillColor"
android:duration="1000"
android:valueFrom="#00ffffff"
android:valueTo="#f9f9fb"
android:valueType="colorType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
<target android:name="path_2">
<aapt:attr name="android:animation">
<objectAnimator
android:propertyName="fillColor"
android:duration="1000"
android:valueFrom="#00ffffff"
android:valueTo="#f9f9fb"
android:valueType="colorType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr>
</target>
</animated-vector>
Loading

0 comments on commit 90af803

Please sign in to comment.