Cant get android URI permissions to work...
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Android;
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using AndroidX.Core.App;
|
||||
using Avalonia;
|
||||
using Avalonia.Android;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Java.Security;
|
||||
using Environment = System.Environment;
|
||||
using Permission = Android.Content.PM.Permission;
|
||||
|
||||
|
||||
namespace CritterFolio.Android;
|
||||
|
||||
@@ -21,6 +28,12 @@ public class MainActivity : AvaloniaMainActivity<App>
|
||||
.WithInterFont();
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle? savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
RequestStorageAccess();
|
||||
}
|
||||
|
||||
public override void OnBackPressed()
|
||||
{
|
||||
if (Sys.Navigation != null && Sys.Navigation.IsLastPage())
|
||||
@@ -38,4 +51,39 @@ public class MainActivity : AvaloniaMainActivity<App>
|
||||
Sys.Navigation?.PopPage();
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestStorageAccess()
|
||||
{
|
||||
string[] permissions;
|
||||
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
|
||||
{
|
||||
permissions = new string[]
|
||||
{
|
||||
Manifest.Permission.ReadMediaImages,
|
||||
Manifest.Permission.ReadMediaVideo
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
permissions = new string[] { Manifest.Permission.ReadExternalStorage };
|
||||
}
|
||||
|
||||
List<string> permissionsToRequest = new List<string>();
|
||||
|
||||
foreach (var permission in permissions)
|
||||
{
|
||||
if (CheckSelfPermission(permission) != Permission.Granted)
|
||||
{
|
||||
permissionsToRequest.Add(permission);
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionsToRequest.Count > 0)
|
||||
{
|
||||
RequestPermissions(permissionsToRequest.ToArray(), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?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" />-->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<application android:label="CritterFolio" android:icon="@drawable/Icon" />
|
||||
</manifest>
|
||||
|
||||
@@ -40,6 +40,7 @@ public partial class App : Application
|
||||
// Other init stuff here
|
||||
Directory.CreateDirectory(Sys.UserDataPath);
|
||||
Directory.CreateDirectory(Path.Combine(Sys.UserDataPath, "ImageCache"));
|
||||
Directory.CreateDirectory(Path.Combine(Sys.UserDataPath, "DocumentCache"));
|
||||
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
mc:Ignorable="d" d:DesignWidth="720" d:DesignHeight="1280"
|
||||
x:Class="CritterFolio.Controls.DocumentItem">
|
||||
<Grid HorizontalAlignment="Stretch" Height="50"
|
||||
Background="{DynamicResource AccentBackground}"
|
||||
@@ -20,7 +20,9 @@
|
||||
Content="Document"
|
||||
Foreground="{DynamicResource PrimaryForeground}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="20" />
|
||||
FontSize="20"
|
||||
MaxWidth="150"
|
||||
/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button x:Name="ViewButton" Classes="icon" Content="" />
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace CritterFolio.Controls;
|
||||
public partial class DocumentItem : UserControl
|
||||
{
|
||||
private Document? _document;
|
||||
|
||||
public event EventHandler? DocumentWasDeleted;
|
||||
|
||||
public DocumentItem()
|
||||
{
|
||||
@@ -35,7 +37,10 @@ public partial class DocumentItem : UserControl
|
||||
if (File.Exists(_document.Path))
|
||||
{
|
||||
var result = await DialogHelper.ShowConfirmationDialog("Are you sure you want to delete this document?");
|
||||
if (result) File.Delete(_document.Path);
|
||||
if (!result) return;
|
||||
await DatabaseService.DeleteDocument(_document.Id);
|
||||
File.Delete(_document.Path);
|
||||
DocumentWasDeleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -46,28 +51,48 @@ public partial class DocumentItem : UserControl
|
||||
private async void ViewButtonOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_document is null) return;
|
||||
if (File.Exists(_document.Path))
|
||||
{
|
||||
var launcher = TopLevel.GetTopLevel(this)?.Launcher;
|
||||
|
||||
if (launcher != null)
|
||||
{
|
||||
var launchResult = await launcher.LaunchUriAsync(new Uri(_document.Path));
|
||||
if (launchResult) return;
|
||||
await DialogHelper.ShowMessage($"Couldn't launch the file: {_document.Path}");
|
||||
Logger.LogToFile($"[ERROR] Couldn't launch the file: {_document.Path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
await DialogHelper.ShowMessage("Launcher was null for some reason. Please report this to the developer.");
|
||||
Logger.LogToFile("Launcher was null for some reason. Please report this to the developer.");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
if (!File.Exists(_document.Path))
|
||||
{
|
||||
await DialogHelper.ShowMessage("Document does not exist");
|
||||
Logger.LogToFile("Document does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel?.Launcher is null)
|
||||
{
|
||||
await DialogHelper.ShowMessage("Launcher unavailable");
|
||||
Logger.LogToFile("Launcher unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var storageFile =
|
||||
await topLevel.StorageProvider.TryGetFileFromPathAsync(new Uri(_document.Path));
|
||||
|
||||
if (storageFile is null)
|
||||
{
|
||||
await DialogHelper.ShowMessage("Unable to access document");
|
||||
Logger.LogToFile("Unable to access document");
|
||||
return;
|
||||
}
|
||||
|
||||
var success = await topLevel.Launcher.LaunchFileAsync(storageFile);
|
||||
|
||||
if (success) return;
|
||||
await DialogHelper.ShowMessage($"Couldn't open file");
|
||||
Logger.LogToFile($"[ERROR] Couldn't open file: {_document.Path}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await DialogHelper.ShowMessage($"[ERROR] Couldn't open {_document.Path}: Due to current limitations with Android's newer external permissions policies. This will be fixed in a later release.");
|
||||
Logger.LogToFile($"[ERROR] Couldn't open file: {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,9 +41,10 @@
|
||||
|
||||
<Grid ColumnDefinitions="Auto, *, Auto" Margin="0, 10">
|
||||
<Label Grid.Column="0" Classes="heading1">Documents</Label>
|
||||
<Button Grid.Column="2">Add New</Button>
|
||||
<Button x:Name="AddDocButton" Grid.Column="2">Add New</Button>
|
||||
</Grid>
|
||||
|
||||
<StackPanel x:Name="DocsStack" HorizontalAlignment="Stretch" Margin="20, 5"/>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -9,6 +9,7 @@ using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CritterFolio.Controls;
|
||||
using CritterFolio.DataModels;
|
||||
using CritterFolio.Services;
|
||||
|
||||
@@ -18,6 +19,7 @@ public partial class CritterPage : Page
|
||||
{
|
||||
private Critter? _critter;
|
||||
private List<CritterComboBoxDisplay> _critterDisplays = [];
|
||||
private List<Document> _documents = [];
|
||||
|
||||
public CritterPage()
|
||||
{
|
||||
@@ -31,9 +33,13 @@ public partial class CritterPage : Page
|
||||
PfpButton.Click += OpenFileButton_Clicked;
|
||||
GenderOption.ItemsSource = Enum.GetNames(typeof(Gender));
|
||||
|
||||
AddDocButton.Click += AddDocButtonOnClick;
|
||||
|
||||
await UpdateInfo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
@@ -125,8 +131,36 @@ public partial class CritterPage : Page
|
||||
Console.WriteLine($"Pfp image for {_critter.Id} was moved, deleted, or is corrupt.");
|
||||
}
|
||||
}
|
||||
|
||||
RegenDocsList();
|
||||
}
|
||||
|
||||
|
||||
private async void RegenDocsList()
|
||||
{
|
||||
_documents.Clear();
|
||||
var docs = await DatabaseService.GetAllDocumentsForCritter(_critter.Id);
|
||||
_documents.AddRange(docs);
|
||||
|
||||
DocsStack.Children.Clear();
|
||||
foreach (var document in _documents)
|
||||
{
|
||||
CreateDocItem(document);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDocItem(Document document)
|
||||
{
|
||||
var item = new DocumentItem();
|
||||
item.Init(document);
|
||||
DocsStack.Children.Add(item);
|
||||
|
||||
item.DocumentWasDeleted += async (sender, args) =>
|
||||
{
|
||||
RegenDocsList();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void CreateHeaderButtons()
|
||||
{
|
||||
var saveBttn = new Button()
|
||||
@@ -264,6 +298,7 @@ public partial class CritterPage : Page
|
||||
private async void OpenFileButton_Clicked(object? sender, RoutedEventArgs args)
|
||||
{
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is null) return;
|
||||
|
||||
var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
@@ -289,8 +324,41 @@ public partial class CritterPage : Page
|
||||
{
|
||||
await DialogHelper.ShowMessage(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async void AddDocButtonOnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_critter is null) return;
|
||||
|
||||
var topLevel = TopLevel.GetTopLevel(this);
|
||||
if (topLevel is null) return;
|
||||
|
||||
var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
Title = "Add Document",
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
if (files.Count == 0) return;
|
||||
var file = files[0];
|
||||
|
||||
var newFileLocation = Path.Combine(Sys.UserDataPath, "DocumentCache", $"{_critter.Id}-{_critter.Name}-{file.Name}");
|
||||
|
||||
await using var sourceStream = await file.OpenReadAsync();
|
||||
await using var destStream = File.Create(newFileLocation);
|
||||
await sourceStream.CopyToAsync(destStream);
|
||||
|
||||
var doc = new Document()
|
||||
{
|
||||
Name = file.Name,
|
||||
CritterId = _critter.Id,
|
||||
Path = newFileLocation,
|
||||
Description = ""
|
||||
};
|
||||
|
||||
await DatabaseService.AddDocument(doc);
|
||||
|
||||
CreateDocItem(doc);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace CritterFolio.Services;
|
||||
|
||||
public static class AndroidPermissions
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user