diff --git a/Billing.Shared/App.cs b/Billing.Shared/App.cs index dc9cd94..cc0fd7f 100644 --- a/Billing.Shared/App.cs +++ b/Billing.Shared/App.cs @@ -13,6 +13,7 @@ namespace Billing { public class App : Application { + internal const string NewBillAction = "/newbill"; private const string SaveLocationKey = nameof(SaveLocationKey); public static AppTheme CurrentTheme { get; private set; } @@ -21,17 +22,27 @@ namespace Billing public static List<Account> Accounts => accounts ??= new List<Account>(); public static List<Category> Categories => categories ??= new List<Category>(); public static bool SaveLocation => saveLocation; + public static string MainRoute => mainRoute; private static List<Bill> bills; private static List<Account> accounts; private static List<Category> categories; private static bool saveLocation; + private static string mainRoute; private string initialUrl; public App(string url = null) { - initialUrl = url; + if (url == NewBillAction) + { + mainRoute = "//Bills/Details"; + } + else + { + mainRoute = "//Bills"; + initialUrl = url; + } CurrentCulture = new PlatformCulture(); InitResources(); diff --git a/Billing.Shared/MainShell.xaml.cs b/Billing.Shared/MainShell.xaml.cs index 8146bcc..d571a06 100644 --- a/Billing.Shared/MainShell.xaml.cs +++ b/Billing.Shared/MainShell.xaml.cs @@ -1,3 +1,4 @@ +using Billing.Views; using Xamarin.Forms; namespace Billing @@ -6,6 +7,8 @@ namespace Billing { public MainShell() { + Routing.RegisterRoute("Bills/Details", typeof(AddBillPage)); + InitializeComponent(); } } diff --git a/Billing.Shared/SplashPage.xaml.cs b/Billing.Shared/SplashPage.xaml.cs index 1533529..0837cb4 100644 --- a/Billing.Shared/SplashPage.xaml.cs +++ b/Billing.Shared/SplashPage.xaml.cs @@ -14,7 +14,7 @@ namespace Billing { await App.InitializeData(); - await Shell.Current.GoToAsync("//Bills"); + await Shell.Current.GoToAsync(App.MainRoute); } } } diff --git a/Billing.Shared/Store/StoreHelper.cs b/Billing.Shared/Store/StoreHelper.cs index 7457b32..c115d50 100644 --- a/Billing.Shared/Store/StoreHelper.cs +++ b/Billing.Shared/Store/StoreHelper.cs @@ -38,6 +38,7 @@ namespace Billing.Store catch (Exception ex) { Helper.Error("database.close", ex); + return false; } try { @@ -54,12 +55,13 @@ namespace Billing.Store SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache); + return true; } catch (Exception ex) { Helper.Error("database.connect", ex); + return false; } - return true; } public static readonly AsyncLazy<StoreHelper> Instance = new(async () => diff --git a/Billing.Shared/Views/AddBillPage.xaml.cs b/Billing.Shared/Views/AddBillPage.xaml.cs index 480ace3..6b328ec 100644 --- a/Billing.Shared/Views/AddBillPage.xaml.cs +++ b/Billing.Shared/Views/AddBillPage.xaml.cs @@ -70,6 +70,7 @@ namespace Billing.Views private CancellationTokenSource tokenSource; private Location location; + public AddBillPage() : this(DateTime.Today) { } public AddBillPage(DateTime date) { createDate = date; diff --git a/Billing.Shared/Views/RankPage.xaml b/Billing.Shared/Views/RankPage.xaml index e53e388..7797798 100644 --- a/Billing.Shared/Views/RankPage.xaml +++ b/Billing.Shared/Views/RankPage.xaml @@ -13,25 +13,24 @@ Shell.TabBarIsVisible="True"> <Shell.TitleView> - <StackLayout Orientation="Horizontal" Spacing="10"> + <Grid ColumnSpacing="10" ColumnDefinitions="30, *, 30"> <ui:TintImageButton Source="left.png" WidthRequest="20" HeightRequest="20" - VerticalOptions="Center" HorizontalOptions="Start" + VerticalOptions="Center" HorizontalOptions="Center" Command="{Binding LeftCommand}"/> - <Label Text="{Binding Title}" + <Label Grid.Column="1" Text="{Binding Title}" TextColor="{DynamicResource PrimaryColor}" FontSize="{OnPlatform Android=20, iOS=18}" FontFamily="{x:Static ui:Definition.SemiBoldFontFamily}" VerticalOptions="Center" - HorizontalOptions="FillAndExpand" - HorizontalTextAlignment="Center"> + HorizontalOptions="Center"> <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding FilterCommand}"/> </Label.GestureRecognizers> </Label> - <ui:TintImageButton Source="right.png" WidthRequest="20" HeightRequest="20" - VerticalOptions="Center" HorizontalOptions="End" + <ui:TintImageButton Grid.Column="2" Source="right.png" WidthRequest="20" HeightRequest="20" + VerticalOptions="Center" HorizontalOptions="Center" Command="{Binding RightCommand}"/> - </StackLayout> + </Grid> </Shell.TitleView> <!--<ContentPage.ToolbarItems> diff --git a/Billing/Billing.Android/Billing.Android.csproj b/Billing/Billing.Android/Billing.Android.csproj index 9a73377..e18038b 100644 --- a/Billing/Billing.Android/Billing.Android.csproj +++ b/Billing/Billing.Android/Billing.Android.csproj @@ -607,7 +607,9 @@ <ItemGroup> <AndroidResource Include="Resources\drawable\left.png" /> </ItemGroup> - <ItemGroup /> + <ItemGroup> + <AndroidResource Include="Resources\xml\shortcuts.xml" /> + </ItemGroup> <Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> </Project> \ No newline at end of file diff --git a/Billing/Billing.Android/MainActivity.cs b/Billing/Billing.Android/MainActivity.cs index edb5074..69f75be 100644 --- a/Billing/Billing.Android/MainActivity.cs +++ b/Billing/Billing.Android/MainActivity.cs @@ -4,7 +4,6 @@ using Android.Content.PM; using Android.Runtime; using Android.OS; using Android.Net; -using System.Collections.Generic; using Android.Provider; using Android.Database; @@ -26,7 +25,14 @@ namespace Billing.Droid string url; if (Intent.ActionView.Equals(Intent.Action) && Intent.Data is Uri uri) { - url = GetFilePath(BaseContext, uri); + if (uri.Authority == "org.tsanie.billing.shortcuts") + { + url = uri.Path; + } + else + { + url = GetFilePath(BaseContext, uri); + } } else { @@ -82,6 +88,15 @@ namespace Billing.Droid } else if (uri.Scheme == "content") { + if (uri.Authority == "com.speedsoftware.rootexplorer.fileprovider") + { + var path = uri.Path; + if (path.StartsWith("/root/")) + { + return path[5..]; + } + return path; + } return GetDataColumn(context, uri, null, null); } else if (uri.Scheme == "file") diff --git a/Billing/Billing.Android/Properties/AndroidManifest.xml b/Billing/Billing.Android/Properties/AndroidManifest.xml index a984595..9bd9c43 100644 --- a/Billing/Billing.Android/Properties/AndroidManifest.xml +++ b/Billing/Billing.Android/Properties/AndroidManifest.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.2.317" package="org.tsanie.billing" android:installLocation="auto" android:versionCode="16"> <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="30" /> - <application android:label="@string/applabel" android:theme="@style/MainTheme"></application> + <application android:label="@string/applabel" android:theme="@style/MainTheme" android:requestLegacyExternalStorage="true"></application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <queries> <intent> diff --git a/Billing/Billing.Android/Resources/Resource.designer.cs b/Billing/Billing.Android/Resources/Resource.designer.cs index f81d669..08a693c 100644 --- a/Billing/Billing.Android/Resources/Resource.designer.cs +++ b/Billing/Billing.Android/Resources/Resource.designer.cs @@ -14,7 +14,7 @@ namespace Billing.Droid { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.2.0.155")] public partial class Resource { @@ -21803,43 +21803,46 @@ namespace Billing.Droid public const int nav_app_bar_open_drawer_description = 2131624042; // aapt resource value: 0x7F0E006B - public const int not_set = 2131624043; + public const int newbill = 2131624043; // aapt resource value: 0x7F0E006C - public const int overflow_tab_title = 2131624044; + public const int not_set = 2131624044; // aapt resource value: 0x7F0E006D - public const int password_toggle_content_description = 2131624045; + public const int overflow_tab_title = 2131624045; // aapt resource value: 0x7F0E006E - public const int path_password_eye = 2131624046; + public const int password_toggle_content_description = 2131624046; // aapt resource value: 0x7F0E006F - public const int path_password_eye_mask_strike_through = 2131624047; + public const int path_password_eye = 2131624047; // aapt resource value: 0x7F0E0070 - public const int path_password_eye_mask_visible = 2131624048; + public const int path_password_eye_mask_strike_through = 2131624048; // aapt resource value: 0x7F0E0071 - public const int path_password_strike_through = 2131624049; + public const int path_password_eye_mask_visible = 2131624049; // aapt resource value: 0x7F0E0072 - public const int preference_copied = 2131624050; + public const int path_password_strike_through = 2131624050; // aapt resource value: 0x7F0E0073 - public const int search_menu_title = 2131624051; + public const int preference_copied = 2131624051; // aapt resource value: 0x7F0E0074 - public const int status_bar_notification_info_overflow = 2131624052; + public const int search_menu_title = 2131624052; // aapt resource value: 0x7F0E0075 - public const int summary_collapsed_preference_list = 2131624053; + public const int status_bar_notification_info_overflow = 2131624053; // aapt resource value: 0x7F0E0076 - public const int v7_preference_off = 2131624054; + public const int summary_collapsed_preference_list = 2131624054; // aapt resource value: 0x7F0E0077 - public const int v7_preference_on = 2131624055; + public const int v7_preference_off = 2131624055; + + // aapt resource value: 0x7F0E0078 + public const int v7_preference_on = 2131624056; static String() { @@ -32297,22 +32300,25 @@ namespace Billing.Droid public const int image_share_filepaths = 2131820544; // aapt resource value: 0x7F110001 - public const int standalone_badge = 2131820545; + public const int shortcuts = 2131820545; // aapt resource value: 0x7F110002 - public const int standalone_badge_gravity_bottom_end = 2131820546; + public const int standalone_badge = 2131820546; // aapt resource value: 0x7F110003 - public const int standalone_badge_gravity_bottom_start = 2131820547; + public const int standalone_badge_gravity_bottom_end = 2131820547; // aapt resource value: 0x7F110004 - public const int standalone_badge_gravity_top_start = 2131820548; + public const int standalone_badge_gravity_bottom_start = 2131820548; // aapt resource value: 0x7F110005 - public const int standalone_badge_offset = 2131820549; + public const int standalone_badge_gravity_top_start = 2131820549; // aapt resource value: 0x7F110006 - public const int xamarin_essentials_fileprovider_file_paths = 2131820550; + public const int standalone_badge_offset = 2131820550; + + // aapt resource value: 0x7F110007 + public const int xamarin_essentials_fileprovider_file_paths = 2131820551; static Xml() { diff --git a/Billing/Billing.Android/Resources/values-zh-rCN/strings.xml b/Billing/Billing.Android/Resources/values-zh-rCN/strings.xml index 25b7ec0..2273f6d 100644 --- a/Billing/Billing.Android/Resources/values-zh-rCN/strings.xml +++ b/Billing/Billing.Android/Resources/values-zh-rCN/strings.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> <resources> <string name="applabel">记账本</string> + <string name="newbill">记录一条</string> </resources> \ No newline at end of file diff --git a/Billing/Billing.Android/Resources/values/strings.xml b/Billing/Billing.Android/Resources/values/strings.xml index d70d42c..ac2b1dd 100644 --- a/Billing/Billing.Android/Resources/values/strings.xml +++ b/Billing/Billing.Android/Resources/values/strings.xml @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> <resources> <string name="applabel">Billing</string> + <string name="newbill">Write a bill</string> </resources> \ No newline at end of file diff --git a/Billing/Billing.Android/Resources/xml/shortcuts.xml b/Billing/Billing.Android/Resources/xml/shortcuts.xml new file mode 100644 index 0000000..43f9856 --- /dev/null +++ b/Billing/Billing.Android/Resources/xml/shortcuts.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"> + <shortcut android:shortcutId="newbill" android:enabled="true" android:icon="@drawable/daily" android:shortcutShortLabel="@string/newbill" android:shortcutLongLabel="@string/newbill" android:shortcutDisabledMessage="@string/newbill"> + <intent android:action="android.intent.action.VIEW" android:targetPackage="org.tsanie.billing" android:targetClass="org.tsanie.billing.SplashScreen" android:data="content://org.tsanie.billing.shortcuts/newbill" /> + </shortcut> +</shortcuts> \ No newline at end of file diff --git a/Billing/Billing.Android/SplashActivity.cs b/Billing/Billing.Android/SplashActivity.cs index 354a998..a6c9ea0 100644 --- a/Billing/Billing.Android/SplashActivity.cs +++ b/Billing/Billing.Android/SplashActivity.cs @@ -18,6 +18,9 @@ namespace Billing.Droid //DataScheme = "file", DataMimeType = "*/*", DataPathPattern = ".*\\.db3")] + [MetaData( + "android.app.shortcuts", + Resource = "@xml/shortcuts")] public class SplashActivity : AppCompatActivity { public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState) diff --git a/Billing/Billing.iOS/AppDelegate.cs b/Billing/Billing.iOS/AppDelegate.cs index 2e73242..02fb032 100644 --- a/Billing/Billing.iOS/AppDelegate.cs +++ b/Billing/Billing.iOS/AppDelegate.cs @@ -1,4 +1,5 @@ using Foundation; +using ObjCRuntime; using UIKit; namespace Billing.iOS @@ -19,7 +20,16 @@ namespace Billing.iOS public override bool FinishedLaunching(UIApplication app, NSDictionary options) { Xamarin.Forms.Forms.Init(); - LoadApplication(new App()); + string action; + if (options != null && options.TryGetValue(UIApplication.LaunchOptionsShortcutItemKey, out var obj) && obj is UIApplicationShortcutItem shortcut) + { + action = "/" + shortcut.Type; + } + else + { + action = null; + } + LoadApplication(new App(action)); return base.FinishedLaunching(app, options); } @@ -32,5 +42,14 @@ namespace Billing.iOS } return false; } + + public override void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler) + { + if (shortcutItem == null || string.IsNullOrEmpty(shortcutItem.Type)) + { + return; + } + Xamarin.Essentials.MainThread.BeginInvokeOnMainThread(async () => await Xamarin.Forms.Shell.Current.GoToAsync("//Bills/Details")); + } } } \ No newline at end of file diff --git a/Billing/Billing.iOS/Base.lproj/InfoPlist.strings b/Billing/Billing.iOS/Base.lproj/InfoPlist.strings index 19f66b9..66d1434 100644 --- a/Billing/Billing.iOS/Base.lproj/InfoPlist.strings +++ b/Billing/Billing.iOS/Base.lproj/InfoPlist.strings @@ -1,2 +1,3 @@ "CFBundleDisplayName" = "Billing"; +"BillingShortcutNew" = "Write a bill"; "NSLocationWhenInUseUsageDescription" = "When "Save Location" is checked, the Billing app needs to access the location."; \ No newline at end of file diff --git a/Billing/Billing.iOS/Info.plist b/Billing/Billing.iOS/Info.plist index dce218b..8d34333 100644 --- a/Billing/Billing.iOS/Info.plist +++ b/Billing/Billing.iOS/Info.plist @@ -93,5 +93,16 @@ <true/> <key>NSLocationWhenInUseUsageDescription</key> <string>When "Save Location" is checked, the Billing app needs to access the location.</string> + <key>UIApplicationShortcutItems</key> + <array> + <dict> + <key>UIApplicationShortcutItemIconType</key> + <string>UIApplicationShortcutIconTypeCompose</string> + <key>UIApplicationShortcutItemTitle</key> + <string>BillingShortcutNew</string> + <key>UIApplicationShortcutItemType</key> + <string>newbill</string> + </dict> + </array> </dict> </plist> diff --git a/Billing/Billing.iOS/zh-Hans.lproj/InfoPlist.strings b/Billing/Billing.iOS/zh-Hans.lproj/InfoPlist.strings index e009fa3..f8e70c2 100644 --- a/Billing/Billing.iOS/zh-Hans.lproj/InfoPlist.strings +++ b/Billing/Billing.iOS/zh-Hans.lproj/InfoPlist.strings @@ -1,2 +1,3 @@ "CFBundleDisplayName" = "记账本"; +"BillingShortcutNew" = "记录一条"; "NSLocationWhenInUseUsageDescription" = "当选中“保存位置”时,记账本需要访问位置信息。"; \ No newline at end of file