Compare commits

..

No commits in common. "master" and "1.6.0+18" have entirely different histories.

36 changed files with 447 additions and 1041 deletions

14
.drone.yml Normal file
View file

@ -0,0 +1,14 @@
kind: pipeline
type: docker
name: default
steps:
- name: build
image: cirrusci/flutter:3.3.10
commands:
- flutter doctor
- flutter pub get
- flutter pub outdated
- flutter packages pub run build_runner build --delete-conflicting-outputs
- flutter analyze --no-pub --no-current-package lib/
- flutter build apk --debug

View file

@ -1,21 +0,0 @@
on: [ push ]
jobs:
build:
runs-on: docker
container:
image: ghcr.io/cirruslabs/flutter:3.24.4
steps:
- name: Prepare requirements
run: |
apt-get update
apt-get install -y nodejs npm git
rm -rf /var/lib/apt/lists/*
- uses: actions/checkout@v3
- name: Build
run: |
flutter doctor
flutter pub get
flutter pub outdated
flutter packages pub run build_runner build --delete-conflicting-outputs
flutter analyze --no-pub --no-current-package lib/
flutter build apk --debug

1
.gitignore vendored
View file

@ -1,7 +1,6 @@
# Miscellaneous
*.class
*.lock
!Gemfile.lock
!pubspec.lock
*.log
*.pyc

View file

@ -1,23 +1,5 @@
# CHANGELOG
## 1.6.4+22 - 2024/11/01
* Dependency updates
* Internal build updates
## 1.6.3+21
* Fixed not receiving share requests from other applications
## 1.6.2+20
* Updated internal dependencies
* Moved progress indicator of _Show Configuration_ into the underlying button
* Bumped Android minSdk to `30` (Android 11)
* Bumped Android targetSdk to `34` (Android 14)
* Fixed permission service not handling Android SDK 33 correctly
* Fixed permission service not being started during application start
## 1.6.1+19
* Updated internal dependencies
## 1.6.0+18
* Fixed input colors in login view when using dark theme
* Added removal of individual files selected for upload

View file

@ -2,8 +2,7 @@
A mobile flutter app for [FileBin](https://git.server-speed.net/users/flo/filebin/).
Available on the [Play Store](https://play.google.com/store/apps/details?id=de.varakh.fbmobile) and
[IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/de.varakh.fbmobile/).
Available on the [Play Store](https://play.google.com/store/apps/details?id=de.varakh.fbmobile).
The main git repository is hosted at **[https://git.myservermanager.com/varakh/fbmobile](https://git.myservermanager.com/varakh/fbmobile)**.
Other repositories are mirrors and pull requests, issues, and planning are managed there.
@ -95,7 +94,7 @@ profiles. They're stored in a separate git repository and are encrypted.
You need access to the git repository in which those private files reside.
#### Usage / doing the actual release
#### Usage
Go into the platform directory you want to build for, e.g. `ios/` or `android/` and then look into the
`Fastlane` file which lanes are present. Run a lane via `fastlane <platform> <lane>`, e.g. use the
@ -105,37 +104,13 @@ following to build for Android `fastlane android build`.
##### Android
It's recommended you set up `fastlane` via `bundler` (you need this to be installed on your machine).
Go into the `android/` sub-directory of the project
```shell
bundle config set --local path 'vendor/bundle'
bundle install
# update fastlane when needed
bundle update fastlane
# build only
bundle exec fastlane android build
# deploy (push BETA to app store)
bundle exec fastlane android beta
# deploy (push to app store)
bundle exec fastlane android deploy
# deploy (build signed fdroid large bundle [no target and abi split])
bundle exec fastlane android build_production_fdroid
```
Use `fastlane android beta` to build and upload a new beta version to the Play Store.
##### iOS
For iOS you need to execute `fastlane ios build` before uploading to testflight with
`fastlane ios beta`.
Probably do the same Ruby/fastlane setup as mentioned under the _Android_ section.
### Release manually (not recommended)
See the following links on how to setup:

2
android/.gitignore vendored
View file

@ -5,5 +5,3 @@ gradle-wrapper.jar
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.bundle
vendor/

View file

@ -1,222 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.7)
base64
nkf
rexml
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.1000.0)
aws-sdk-core (3.211.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.95.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.169.0)
aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
declarative (0.0.20)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (0.112.0)
faraday (1.10.4)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.7)
faraday (>= 0.8.0)
http-cookie (~> 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.225.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0)
colored (~> 1.2)
commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2)
optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
rexml
google-apis-iamcredentials_v1 (0.17.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-playcustomapp_v1 (0.13.0)
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.7.1)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.4.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.31.0)
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.7)
domain_name (~> 0.5)
httpclient (2.8.3)
jmespath (1.6.2)
json (2.7.5)
jwt (2.9.3)
base64
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
nanaimo (0.4.0)
naturally (2.2.1)
nkf (0.2.0)
optparse (0.5.0)
os (1.1.4)
plist (3.7.1)
public_suffix (6.0.1)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.9)
rouge (2.0.7)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
fastlane
BUNDLED WITH
2.5.16

View file

@ -1,9 +1,3 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@ -12,6 +6,11 @@ if (localPropertiesFile.exists()) {
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -22,6 +21,9 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@ -29,9 +31,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 34
namespace "de.varakh.fbmobile"
compileSdkVersion 33
lintOptions {
disable 'InvalidPackage'
@ -39,8 +39,8 @@ android {
defaultConfig {
applicationId "de.varakh.fbmobile"
minSdkVersion 30
targetSdkVersion 34
minSdkVersion 16
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View file

@ -4,10 +4,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<queries>
<intent>

View file

@ -21,41 +21,6 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
@ -65,10 +30,7 @@
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<queries>
<intent>

View file

@ -4,10 +4,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<queries>
<intent>

View file

@ -1,3 +1,14 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.0.4'
}
}
allprojects {
repositories {
google()
@ -13,6 +24,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -11,11 +11,6 @@ platform :android do
sh("#{ENV['PWD']}/fastlane/buildAndroidProduction.sh")
end
desc "Build Production fdroid"
lane :build_production_fdroid do
sh("#{ENV['PWD']}/fastlane/buildAndroidProductionFdroid.sh")
end
desc "Build"
lane :build do
sh("#{ENV['PWD']}/fastlane/buildAndroid.sh")

View file

@ -31,14 +31,6 @@ Build Debug
Build Production
### android build_production_fdroid
```sh
[bundle exec] fastlane android build_production_fdroid
```
Build Production fdroid
### android build
```sh

View file

@ -1,9 +0,0 @@
#!/usr/bin/env sh
cd ../../;
flutter clean && \
flutter pub get &&
flutter packages pub run build_runner build --delete-conflicting-outputs;
flutter build apk --release;
flutter build apk --split-per-abi --release;

View file

@ -1,6 +1,4 @@
agpVersion=8.7.2
kotlinVersion=1.7.10
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

View file

@ -1,7 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

View file

@ -1,25 +1,15 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
include ':app'
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "${agpVersion}" apply false
id "org.jetbrains.kotlin.android" version "${kotlinVersion}" apply false
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
include ":app"

View file

@ -126,6 +126,14 @@
"description": "Could not open '{link}'. Please ensure that you have an application installed which handles opening such link types."
}
},
"permission_service": {
"dialog": {
"title": "Storage permission",
"description": "Storage permission should be granted to the app so that it can work properly. Do you want to grant permission or ignore this message permanently in the future?",
"grant": "Grant",
"ignore": "Ignore"
}
},
"dialog": {
"confirm": "OK",
"cancel": "Cancel"

View file

@ -5,8 +5,8 @@ class RestServiceException extends ServiceException {
final int statusCode;
final dynamic responseBody;
RestServiceException(this.statusCode, {this.responseBody, super.message = null})
: super(code: ErrorCode.restError);
RestServiceException(this.statusCode, {this.responseBody, String? message})
: super(code: ErrorCode.restError, message: message);
@override
String toString() {

View file

@ -8,7 +8,7 @@ import '../services/dialog_service.dart';
class DialogManager extends StatefulWidget {
final Widget? child;
const DialogManager({super.key, this.child});
const DialogManager({Key? key, this.child}) : super(key: key);
@override
_DialogManagerState createState() => _DialogManagerState();

View file

@ -11,7 +11,7 @@ import '../util/logger.dart';
class LifeCycleManager extends StatefulWidget {
final Widget? child;
const LifeCycleManager({super.key, this.child});
const LifeCycleManager({Key? key, this.child}) : super(key: key);
@override
_LifeCycleManagerState createState() => _LifeCycleManagerState();

View file

@ -1,100 +1,108 @@
import 'dart:async';
import 'dart:io' show Platform;
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:logger/logger.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../constants.dart';
import '../../core/datamodels/dialog_response.dart';
import '../../core/services/dialog_service.dart';
import '../../core/services/stoppable_service.dart';
import '../../core/util/logger.dart';
import '../../locator.dart';
import 'storage_service.dart';
class PermissionService extends StoppableService {
final Logger _logger = getLogger();
final DialogService _dialogService = locator<DialogService>();
final StorageService _storageService = locator<StorageService>();
Timer? _serviceCheckTimer;
PermissionStatus? _permissionStatus;
bool _permanentlyIgnored = false;
bool _devicePermissionDialogActive = false;
bool _ownPermissionDialogActive = false;
bool _deviceInformationInitialized = false;
bool _useStoragePermission = true;
PermissionService() {
_devicePermissionDialogActive = true;
PermissionService();
Permission.storage.request().then((status) {
_permissionStatus = status;
if (PermissionStatus.permanentlyDenied == status) {
_permanentlyIgnored = true;
}
}).whenComplete(() {
_logger.d('Initial device request permission finished');
_devicePermissionDialogActive = false;
});
}
Future checkEnabledAndPermission() async {
if (_permanentlyIgnored) {
await _storageService.storeStoragePermissionDialogIgnored();
_permanentlyIgnored = false;
_logger.d('Set permanently ignored permission request');
stop();
}
if (_devicePermissionDialogActive) {
_logger.d('Device permission dialog active, skipping');
return;
}
bool allGranted = false;
bool anyPermanentlyDenied = false;
if (_ownPermissionDialogActive) {
_logger.d('Own permission dialog already active, skipping');
return;
}
// Since Android compileSdk >= 33, "storage" is deprecated
// Instead, request access to all of
// - Permission.photos
// - Permission.videos
// - Permission.audio
//
// For iOS and Android < 33, keep using "storage"
if (_useStoragePermission) {
PermissionStatus storagePermission = await Permission.storage.status;
allGranted = PermissionStatus.granted == storagePermission;
anyPermanentlyDenied =
PermissionStatus.permanentlyDenied == storagePermission;
var ignoredDialog =
await _storageService.hasStoragePermissionDialogIgnored();
if (ignoredDialog) {
_logger.d('Permanently ignored permission request, skipping');
stop();
return;
}
_permissionStatus = await Permission.storage.status;
if (_permissionStatus != PermissionStatus.granted) {
if (_permissionStatus == PermissionStatus.permanentlyDenied) {
await _storageService.storeStoragePermissionDialogIgnored();
return;
}
_ownPermissionDialogActive = true;
DialogResponse response = await _dialogService.showConfirmationDialog(
title: translate('permission_service.dialog.title'),
description: translate('permission_service.dialog.description'),
buttonTitleAccept: translate('permission_service.dialog.grant'),
buttonTitleDeny: translate('permission_service.dialog.ignore'));
if (!response.confirmed!) {
await _storageService.storeStoragePermissionDialogIgnored();
} else {
PermissionStatus photosPermission = await Permission.photos.status;
PermissionStatus videosPermission = await Permission.videos.status;
PermissionStatus audioPermission = await Permission.audio.status;
allGranted = PermissionStatus.granted == photosPermission &&
PermissionStatus.granted == videosPermission &&
PermissionStatus.granted == audioPermission;
anyPermanentlyDenied =
PermissionStatus.permanentlyDenied == photosPermission ||
PermissionStatus.permanentlyDenied == videosPermission ||
PermissionStatus.permanentlyDenied == audioPermission;
}
// show warning to user to manually handle, don't enforce it over and over again
if (anyPermanentlyDenied) {
_logger.w(
"At least one required permission has been denied permanently, stopping service");
stop();
return;
}
// all good, stop the permission service
if (allGranted) {
_logger.d("All permissions have been granted, stopping service");
stop();
return;
}
// not all have been granted, show OS dialog
_logger.d(
"Not all permissions have been granted yet, initializing permission dialog");
_devicePermissionDialogActive = true;
Permission.storage.request().then((status) async {
if (PermissionStatus.permanentlyDenied == status) {
await _storageService.storeStoragePermissionDialogIgnored();
}
}).whenComplete(() {
_logger.d('Device request permission finished');
_devicePermissionDialogActive = false;
});
}
if (_useStoragePermission) {
await [Permission.storage].request().whenComplete(() {
_logger.d('Device request permission finished');
_devicePermissionDialogActive = false;
});
_ownPermissionDialogActive = false;
} else {
await [Permission.photos, Permission.videos, Permission.audio]
.request()
.whenComplete(() {
_logger.d('Device request permission finished');
_devicePermissionDialogActive = false;
});
await _storageService.storeStoragePermissionDialogIgnored();
}
}
@override
Future start() async {
super.start();
await _determineDeviceInfo();
await checkEnabledAndPermission();
_serviceCheckTimer = Timer.periodic(
@ -116,29 +124,6 @@ class PermissionService extends StoppableService {
_logger.d('PermissionService stopped');
}
Future _determineDeviceInfo() async {
if (_deviceInformationInitialized) {
_logger.d('Device information already initialized, skipping');
return;
}
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
if (Platform.isAndroid) {
final androidInfo = await deviceInfoPlugin.androidInfo;
if (androidInfo.version.sdkInt >= 33) {
_useStoragePermission = false;
}
}
if (_useStoragePermission) {
_logger.d('Device requires [storage] permission');
} else {
_logger.d('Device requires [photos,videos,audio] permission');
}
_deviceInformationInitialized = true;
}
void _removeServiceCheckTimer() {
if (_serviceCheckTimer != null) {
_serviceCheckTimer!.cancel();

View file

@ -7,6 +7,8 @@ import '../models/session.dart';
class StorageService {
static const _sessionKey = 'session';
static const _lastUrlKey = 'last_url';
static const _storagePermissionDialogIgnoredKey =
'storage_permission_ignored';
Future<bool> storeLastUrl(String url) {
return _store(_lastUrlKey, url);
@ -37,6 +39,14 @@ class StorageService {
return _remove(_sessionKey);
}
Future<bool> storeStoragePermissionDialogIgnored() {
return _store(_storagePermissionDialogIgnoredKey, true.toString());
}
Future<bool> hasStoragePermissionDialogIgnored() {
return _exists(_storagePermissionDialogIgnoredKey);
}
Future<bool> _exists(String key) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.containsKey(key);

View file

@ -108,7 +108,7 @@ class HistoryModel extends BaseModel {
} else {
errorMessage = translate('app.unknown_error');
setStateView(ViewState.idle);
_logger.e('An unknown error occurred', error: e);
_logger.e('An unknown error occurred', e);
rethrow;
}
}
@ -156,7 +156,7 @@ class HistoryModel extends BaseModel {
} else {
errorMessage = translate('app.unknown_error');
setStateView(ViewState.idle);
_logger.e('An unknown error occurred', error: e);
_logger.e('An unknown error occurred', e);
rethrow;
}
}

View file

@ -163,7 +163,7 @@ class LoginModel extends BaseModel {
errorMessage = translate('app.unknown_error');
_sessionService.logout();
setStateView(ViewState.idle);
_logger.e('An unknown error occurred', error: e);
_logger.e('An unknown error occurred', e);
rethrow;
}

View file

@ -77,7 +77,7 @@ class ProfileModel extends BaseModel {
setStateBoolValue(_configurationButtonLoading, false);
_sessionService.logout();
setStateBoolValue(_configurationButtonLoading, false);
_logger.e('An unknown error occurred', error: e);
_logger.e('An unknown error occurred', e);
rethrow;
}
}

View file

@ -1,4 +1,3 @@
import 'package:fbmobile/core/services/permission_service.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../locator.dart';
@ -10,18 +9,16 @@ import 'base_model.dart';
class StartUpViewModel extends BaseModel {
final SessionService _sessionService = locator<SessionService>();
final PermissionService _permissionService = locator<PermissionService>();
final NavigationService _navigationService = locator<NavigationService>();
Future handleStartUpLogic() async {
setStateView(ViewState.busy);
setStateMessage(translate('startup.init'));
await Future.delayed(const Duration(milliseconds: 100));
await Future.delayed(const Duration(milliseconds: 150));
setStateMessage(translate('startup.start_services'));
await _sessionService.start();
await _permissionService.start();
await Future.delayed(const Duration(milliseconds: 100));
await Future.delayed(const Duration(milliseconds: 150));
_navigationService.navigateAndReplaceTo(HomeView.routeName);

View file

@ -152,9 +152,9 @@ class UploadModel extends BaseModel {
))
?.files;
} on PlatformException catch (e) {
_logger.e('Unsupported operation', error: e);
_logger.e('Unsupported operation', e);
} catch (ex) {
_logger.e('An unknown error occurred', error: ex);
_logger.e('An unknown error occurred', ex);
}
loadingPath = false;
@ -238,7 +238,7 @@ class UploadModel extends BaseModel {
errorMessage = translate('app.unknown_error');
setStateMessage(null);
setStateView(ViewState.idle);
_logger.e('An unknown error occurred', error: e);
_logger.e('An unknown error occurred', e);
rethrow;
}
}

View file

@ -91,9 +91,7 @@ class HistoryView extends StatelessWidget {
content: Text(translate('history.copy_link.copied')),
duration: const Duration(seconds: 10),
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
});
}));

View file

@ -53,22 +53,20 @@ class ProfileView extends StatelessWidget {
UIHelper.verticalSpaceMedium(),
Padding(
padding: const EdgeInsets.only(left: 25.0, right: 25.0),
child: ElevatedButton.icon(
icon: model.configLoading
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: blueColor,
strokeWidth: 3,
),
)
: const Icon(Icons.settings, color: blueColor),
child: model.configLoading
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircularProgressIndicator(),
Text(translate('profile.show_config_loading')),
],
))
: ElevatedButton.icon(
icon: const Icon(Icons.settings, color: blueColor),
label: Text(
model.configLoading
? translate('profile.show_config_loading')
: translate('profile.show_config'),
translate('profile.show_config'),
),
onPressed: () async {
await model.showConfig(url);

View file

@ -151,10 +151,8 @@ class UploadView extends StatelessWidget {
duration:
const Duration(seconds: 10),
);
if (context.mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
});
}
},

View file

@ -7,11 +7,12 @@ class MyAppBar extends AppBar {
static final List<Widget> aboutDisabledWidgets = [];
MyAppBar(
{super.key,
{Key? key,
required Widget title,
List<Widget>? actionWidgets,
bool enableAbout = true})
: super(
key: key,
title: Row(children: <Widget>[title]),
actions: _renderIconButtons(actionWidgets, enableAbout));

File diff suppressed because it is too large Load diff

View file

@ -11,47 +11,46 @@ description: A mobile client for FileBin.
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.6.4+22
version: 1.6.0+18
environment:
sdk: '>=3.5.0 <4.0.0'
sdk: '>=2.18.5 <3.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: 1.0.8
cupertino_icons: 1.0.5
flutter_localizations:
sdk: flutter
flutter_translate: 4.1.0
provider: 6.1.2
stacked: 3.4.3
get_it: 7.7.0
logger: 2.4.0
shared_preferences: 2.3.2
http: 1.2.2
flutter_translate: 4.0.3
provider: 6.0.5
stacked: 3.1.0+1
get_it: 7.2.0
logger: 1.1.0
shared_preferences: 2.0.16
http: 0.13.5
validators: 3.0.0
flutter_linkify: 6.0.0
url_launcher: 6.3.1
flutter_linkify: 5.0.2
url_launcher: 6.1.7
expandable: 5.0.1
share_plus: 10.1.1
file_picker: 8.1.3
share_plus: 6.3.0
file_picker: 5.2.5
clipboard: 0.1.3
permission_handler: 11.3.1
package_info_plus: 8.1.0
json_annotation: 4.9.0
dynamic_color: 1.7.0
intl: 0.19.0
path: 1.9.0
flutter_sharing_intent: 1.1.1
device_info_plus: 11.1.0
permission_handler: 10.2.0
package_info_plus: 3.0.2
json_annotation: 4.7.0
dynamic_color: 1.5.4
intl: 0.17.0
path: 1.8.2
flutter_sharing_intent: 1.0.5
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: 2.4.13
built_value_generator: 8.9.2
json_serializable: 6.8.0
flutter_lints: 5.0.0
build_runner: 2.3.3
built_value_generator: 8.4.3
json_serializable: 6.5.4
flutter_lints: 2.0.1
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec

View file

@ -1,38 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":rebaseStalePrs",
":ignoreUnstable",
"group:monorepos",
"group:recommended"
],
"prConcurrentLimit": 0,
"schedule": [
"monthly"
],
"ignorePaths": [
"android/**",
"ios/**"
],
"ignoreDeps": [
"intl",
"path"
],
"packageRules": [
{
"matchUpdateTypes": [
"minor"
],
"groupName": "all minor dependencies",
"groupSlug": "all-minor-deps"
},
{
"matchUpdateTypes": [
"patch"
],
"groupName": "all patch dependencies",
"groupSlug": "all-patch-deps"
}
]
}