Skip to content

Commit

Permalink
Started save image to its own folder in gallery
Browse files Browse the repository at this point in the history
This doesn't build because the min Android sdk doesn't work yet
  • Loading branch information
njs-guy committed Jul 19, 2023
1 parent c8110c6 commit 1afe7d6
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 48 deletions.
7 changes: 7 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ allprojects {
}
}

android {
defaultConfig {
// Defines the minimum API level required to run the app.
minSdkVersion(21)
}
}

rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
Expand Down
116 changes: 82 additions & 34 deletions lib/shared/image_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import 'package:flutter/material.dart';

import 'package:extended_image/extended_image.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gallery_saver/gallery_saver.dart';
import 'package:share_plus/share_plus.dart';

import 'package:thunder/shared/hero.dart';

import 'package:image_gallery_saver/image_gallery_saver.dart';

import 'package:flutter_cache_manager/flutter_cache_manager.dart';

import 'package:path_provider/path_provider.dart';
Expand All @@ -23,7 +22,8 @@ import '../account/bloc/account_bloc.dart';
import '../community/bloc/community_bloc.dart';
import '../core/auth/bloc/auth_bloc.dart';
import '../post/pages/post_page.dart';
import 'package:thunder/post/bloc/post_bloc.dart' as post_bloc; // renamed to prevent clash with VotePostEvent, etc from community_bloc
import 'package:thunder/post/bloc/post_bloc.dart'
as post_bloc; // renamed to prevent clash with VotePostEvent, etc from community_bloc
import '../thunder/bloc/thunder_bloc.dart';

class ImageViewer extends StatefulWidget {
Expand All @@ -44,9 +44,12 @@ class ImageViewer extends StatefulWidget {
State<ImageViewer> createState() => _ImageViewerState();
}

class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin {
GlobalKey<ExtendedImageSlidePageState> slidePagekey = GlobalKey<ExtendedImageSlidePageState>();
final GlobalKey<ScaffoldMessengerState> _imageViewer = GlobalKey<ScaffoldMessengerState>();
class _ImageViewerState extends State<ImageViewer>
with TickerProviderStateMixin {
GlobalKey<ExtendedImageSlidePageState> slidePagekey =
GlobalKey<ExtendedImageSlidePageState>();
final GlobalKey<ScaffoldMessengerState> _imageViewer =
GlobalKey<ScaffoldMessengerState>();
bool downloaded = false;

double slideTransparency = 0.93;
Expand All @@ -65,7 +68,8 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
Future<bool> _requestPermission() async {
bool androidVersionBelow33 = false;
if (Platform.isAndroid) {
androidVersionBelow33 = (await DeviceInfoPlugin().androidInfo).version.sdkInt <= 32;
androidVersionBelow33 =
(await DeviceInfoPlugin().androidInfo).version.sdkInt <= 32;
}

if (androidVersionBelow33) {
Expand All @@ -76,14 +80,18 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
Permission.photosAddOnly,
].request();
}
bool hasPermission = await Permission.photos.isGranted || await Permission.photos.isLimited || await Permission.storage.isGranted || await Permission.storage.isLimited;
bool hasPermission = await Permission.photos.isGranted ||
await Permission.photos.isLimited ||
await Permission.storage.isGranted ||
await Permission.storage.isLimited;

return hasPermission;
}

@override
Widget build(BuildContext context) {
AnimationController animationController = AnimationController(duration: const Duration(milliseconds: 140), vsync: this);
AnimationController animationController = AnimationController(
duration: const Duration(milliseconds: 140), vsync: this);
Function() animationListener = () {};
Animation? animation;

Expand All @@ -107,7 +115,8 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
var offset = state.offset;
var pageSize = state.pageSize;

var scale = offset.distance / Offset(pageSize.width, pageSize.height).distance;
var scale = offset.distance /
Offset(pageSize.width, pageSize.height).distance;

if (state.isSliding) {
setState(() {
Expand All @@ -125,7 +134,8 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
if (state != null) {
var offset = state.offset;
var pageSize = state.pageSize;
return offset.distance.greaterThan(Offset(pageSize.width, pageSize.height).distance / 10);
return offset.distance.greaterThan(
Offset(pageSize.width, pageSize.height).distance / 10);
}
return true;
},
Expand Down Expand Up @@ -174,9 +184,12 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
end = 1;
}
animationListener = () {
state.handleDoubleTap(scale: animation!.value, doubleTapPosition: pointerDownPosition);
state.handleDoubleTap(
scale: animation!.value,
doubleTapPosition: pointerDownPosition);
};
animation = animationController.drive(Tween<double>(begin: begin, end: end));
animation = animationController
.drive(Tween<double>(begin: begin, end: end));

animation!.addListener(animationListener);

Expand All @@ -198,8 +211,10 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
),
),
Container(
decoration: BoxDecoration(color: Colors.black.withOpacity(slideTransparency)),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
decoration: BoxDecoration(
color: Colors.black.withOpacity(slideTransparency)),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom),
child: Row(
children: [
Expanded(flex: 2, child: Container()),
Expand Down Expand Up @@ -257,58 +272,87 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
onPressed: () async {
try {
// Try to get the cached image first
var media = await DefaultCacheManager().getFileFromCache(widget.url!);
var media = await DefaultCacheManager()
.getFileFromCache(widget.url!);
File? mediaFile = media?.file;

if (media == null) {
// Tell user we're downloading the image
SnackBar snackBar = const SnackBar(
content: Text('Downloading media to share...'),
content:
Text('Downloading media to share...'),
behavior: SnackBarBehavior.floating,
);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
_imageViewer.currentState?.clearSnackBars();
_imageViewer.currentState?.showSnackBar(snackBar);
_imageViewer.currentState
?.showSnackBar(snackBar);
});

// Download
mediaFile = await DefaultCacheManager().getSingleFile(widget.url!);
mediaFile = await DefaultCacheManager()
.getSingleFile(widget.url!);

// Hide snackbar
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
_imageViewer.currentState?.clearSnackBars();
});
}

// Share
await Share.shareXFiles([XFile(mediaFile!.path)]);
await Share.shareXFiles(
[XFile(mediaFile!.path)]);
} catch (e) {
// Tell the user that the download failed
SnackBar snackBar = SnackBar(
content: Text('There was an error downloading the media file to share: $e'),
content: Text(
'There was an error downloading the media file to share: $e'),
behavior: SnackBarBehavior.floating,
);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
WidgetsBinding.instance
.addPostFrameCallback((timeStamp) {
_imageViewer.currentState?.clearSnackBars();
_imageViewer.currentState?.showSnackBar(snackBar);
_imageViewer.currentState
?.showSnackBar(snackBar);
});
}
},
icon: const Icon(Icons.share_rounded, semanticLabel: "Comments", color: Colors.white),
icon: const Icon(Icons.share_rounded,
semanticLabel: "Comments", color: Colors.white),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: IconButton(
onPressed: () async {
File file = await DefaultCacheManager().getSingleFile(widget.url);
File file = await DefaultCacheManager()
.getSingleFile(widget.url);

if ((Platform.isAndroid || Platform.isIOS) &&
await _requestPermission()) {
// final directory =
// await getApplicationDocumentsDirectory();

// final result =
// await GallerySaver.saveFile(file.path);

if ((Platform.isAndroid || Platform.isIOS) && await _requestPermission()) {
final result = await ImageGallerySaver.saveFile(file.path);
// debugPrint("Images:");
// debugPrint(directory.path);

setState(() => downloaded = result['isSuccess'] == true);
} else if (Platform.isLinux || Platform.isWindows) {
final filePath = '${(await getApplicationDocumentsDirectory()).path}/ThunderImages/${basename(file.path)}';
// setState(() =>
// downloaded = result['isSuccess'] == true);

GallerySaver.saveImage(file.path,
albumName: "ThunderImages")
.then((value) {
setState(() => downloaded = value as bool);
});
} else if (Platform.isLinux ||
Platform.isWindows) {
final filePath =
'${(await getApplicationDocumentsDirectory()).path}/ThunderImages/${basename(file.path)}';

File(filePath)
..createSync(recursive: true)
Expand All @@ -318,8 +362,12 @@ class _ImageViewerState extends State<ImageViewer> with TickerProviderStateMixin
}
},
icon: downloaded
? const Icon(Icons.check_circle, semanticLabel: 'Downloaded', color: Colors.white)
: const Icon(Icons.download, semanticLabel: "Download", color: Colors.white),
? const Icon(Icons.check_circle,
semanticLabel: 'Downloaded',
color: Colors.white)
: const Icon(Icons.download,
semanticLabel: "Download",
color: Colors.white),
),
),
],
Expand Down
34 changes: 21 additions & 13 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
gallery_saver:
dependency: "direct main"
description:
name: gallery_saver
sha256: df8b7e207ca12d64c71e0710a7ee3bc48aa7206d51cc720716fedb1543a66712
url: "https://pub.dev"
source: hosted
version: "2.3.2"
go_router:
dependency: "direct main"
description:
Expand Down Expand Up @@ -489,14 +497,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.17"
image_gallery_saver:
dependency: "direct main"
description:
name: image_gallery_saver
sha256: "467de169167b5c4e1ddde65395e4653c336a82f760b6700ea295085b7f2dd248"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
image_picker:
dependency: "direct main"
description:
Expand Down Expand Up @@ -631,10 +631,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev"
source: hosted
version: "0.12.15"
version: "0.12.16"
material_color_utilities:
dependency: transitive
description:
Expand Down Expand Up @@ -1044,10 +1044,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: daadc9baabec998b062c9091525aa95786508b1c48e9c30f1f891b8bf6ff2e64
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
url: "https://pub.dev"
source: hosted
version: "0.5.2"
version: "0.6.0"
translator:
dependency: transitive
description:
Expand Down Expand Up @@ -1152,6 +1152,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
web_socket_channel:
dependency: transitive
description:
Expand Down Expand Up @@ -1233,5 +1241,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.1.0-116.0.dev <4.0.0"
dart: ">=3.1.0-185.0.dev <4.0.0"
flutter: ">=3.10.0"
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dependencies:
share_plus: ^7.0.2
flex_color_scheme: ^7.1.2
dynamic_color: ^1.6.5
image_gallery_saver: ^2.0.2
gallery_saver: ^2.3.2
flutter_cache_manager: ^3.3.0
permission_handler: ^10.3.0
path_provider: ^2.0.15
Expand Down

0 comments on commit 1afe7d6

Please sign in to comment.