Skip to content

Commit

Permalink
Merge pull request #13 from ayekaunic/QC-13-Add-Share-PDF-Module
Browse files Browse the repository at this point in the history
QC-13: Add 'Share PDF Module'
  • Loading branch information
ayekaunic authored Aug 13, 2024
2 parents 70f70c4 + 5dcdf8b commit a019b22
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 45 deletions.
12 changes: 11 additions & 1 deletion app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,20 @@ android {
signingConfig signingConfigs.debug
}
}

configurations.all {
resolutionStrategy {
force 'org.jetbrains.kotlin:kotlin-stdlib:1.8.22'
force 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22'
force 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22'
}
}
}

flutter {
source '../..'
}

dependencies {}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
4 changes: 4 additions & 0 deletions app/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<application
android:label="Quran Companion"
android:name="${applicationName}"
Expand Down
11 changes: 11 additions & 0 deletions app/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
buildscript {
ext.kotlin_version = '1.8.22'
repositories {
google()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

allprojects {
repositories {
google()
Expand Down
46 changes: 46 additions & 0 deletions app/assets/svgs/home_banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/lib/global/utils/svg_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ const String appIcon = 'assets/svgs/app_icon.svg';
const String backIcon = 'assets/svgs/back_icon.svg';
const String starIcon = 'assets/svgs/star_icon.svg';
const String noteIcon = 'assets/svgs/note_icon.svg';
const String homeBanner = 'assets/svgs/home_banner.svg';
31 changes: 18 additions & 13 deletions app/lib/global/widgets/ayat_tile/ayat_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ class AyatTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder.reactive(
viewModelBuilder: () => AyatTileViewModel(ayatNumber: number),
viewModelBuilder: () => AyatTileViewModel(
ayatNumber: number,
arabic: arabic,
urdu: urdu,
),
builder: (context, viewModel, child) {
return HorizontalPadding(
padding: 27.w,
Expand All @@ -50,21 +54,22 @@ class AyatTile extends StatelessWidget {
_CustomActionButton(
tooltip: 'Make a note',
onTap: viewModel.onMakeNoteTap,
child: Center(
child: viewModel.isMakingNote
? const _CustomLoadingIndicator()
: SvgPicture.asset(noteIcon),
),
child: viewModel.isMakingNote
? const _CustomLoadingIndicator()
: Center(child: SvgPicture.asset(noteIcon)),
),
_CustomActionButton(
tooltip: 'Save as PDF',
onTap: () {},
tooltip: 'Share as PDF',
onTap: viewModel.onSharePDFTap,
child: SizedBox(
width: 21.w,
child: const Icon(
Icons.save_alt_rounded,
color: green,
),
child: viewModel.isSavingPDF
? const _CustomLoadingIndicator()
: Icon(
Icons.share_outlined,
color: green,
size: 20.w,
),
),
),
const Spacer(),
Expand Down Expand Up @@ -182,7 +187,7 @@ class _CustomActionButton extends StatelessWidget {
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10.r),
splashColor: green.withOpacity(0.1),
splashColor: green.withOpacity(0.12),
child: SizedBox(
width: 30.5.w,
height: 30.5.h,
Expand Down
151 changes: 134 additions & 17 deletions app/lib/global/widgets/ayat_tile/ayat_tile_view_model.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,165 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:quran_companion/global/setup/bottom_sheet_setup.dart';
import 'package:quran_companion/global/setup/snackbar_setup.dart';
import 'package:quran_companion/services_locator.dart';
import 'package:share_plus/share_plus.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:pdf/widgets.dart' as pw;

class AyatTileViewModel extends BaseViewModel {
final BottomSheetService _bottomSheetService = locator<BottomSheetService>();
final SnackbarService _snackbarService = locator<SnackbarService>();

bool _isMakingNote = false;

bool get isMakingNote => _isMakingNote;

final int ayatNumber;
AyatTileViewModel({required this.ayatNumber});

Future<void> onMakeNoteTap() async {
_setIsMakingNote(true);
await _showNoteDialog();
_setIsMakingNote(false);
}

void _setIsMakingNote(bool val) {
_isMakingNote = val;
rebuildUi();
}

bool _isSharingPDF = false;
bool get isSavingPDF => _isSharingPDF;
void _setIsSavingPDF(bool val) {
_isSharingPDF = val;
rebuildUi();
}

final int ayatNumber;
final String arabic;
final String urdu;
AyatTileViewModel({
required this.ayatNumber,
required this.arabic,
required this.urdu,
});

Future<void> _showNoteDialog() async {
SheetResponse? response = await _bottomSheetService.showCustomSheet(
variant: BottomSheetType.note,
barrierDismissible: true,
enableDrag: true,
data: ayatNumber,
);
if (response != null) _showSnackbar(response.confirmed);
if (response != null) {
_snackbarService.showCustomSnackBar(
title: response.confirmed ? 'Success!' : 'Error!',
message: response.confirmed ? 'Note saved.' : 'Unable to save note.',
variant: response.confirmed ? SnackbarType.success : SnackbarType.error,
);
}
}

Future<void> _showSnackbar(bool isNoteSaved) async {
Future<pw.Document> _generateAyatPDF() async {
final pdf = pw.Document();

final fontData = await rootBundle.load('assets/fonts/amiri_bold.ttf');
final ttf = pw.Font.ttf(fontData);

pdf.addPage(
pw.Page(
build: (pw.Context context) => pw.Padding(
padding: const pw.EdgeInsets.all(16),
child: pw.Column(
children: [
pw.Text(
'Quran Companion (Ayat #$ayatNumber)',
style: pw.TextStyle(
fontSize: 24,
fontWeight: pw.FontWeight.bold,
),
),
pw.SizedBox(height: 16),
pw.Align(
alignment: pw.Alignment.centerRight,
child: pw.Text(
arabic,
style: pw.TextStyle(
fontSize: 20,
font: ttf,
),
textAlign: pw.TextAlign.right,
),
),
pw.SizedBox(height: 16),
pw.Align(
alignment: pw.Alignment.centerRight,
child: pw.Text(
urdu,
style: pw.TextStyle(
fontSize: 18,
font: ttf,
),
textAlign: pw.TextAlign.right,
),
),
],
),
),
),
);

return pdf;
}

Future<File> _savePDFToFile(pw.Document pdf) async {
final directory = Platform.isIOS
? await path_provider.getApplicationDocumentsDirectory()
: await path_provider.getExternalStorageDirectory();

final file =
File('${directory!.path}/Quran-Companion-Ayat#$ayatNumber.pdf');
await file.writeAsBytes(await pdf.save());

return file;
}

Future<void> _sharePDFFile(File file) async {
final result = await Share.shareXFiles(
[XFile(file.path)],
subject: 'Quran Companion - Ayat#$ayatNumber',
text: 'Quran Companion - Ayat#$ayatNumber',
);

bool sharedSuccessfully = result.status == ShareResultStatus.success;

_snackbarService.showCustomSnackBar(
title: isNoteSaved ? 'Success!' : 'Error!',
message: isNoteSaved ? 'Note saved' : 'Unable to save note',
variant: isNoteSaved ? SnackbarType.success : SnackbarType.error,
title: sharedSuccessfully ? 'Success!' : 'Error!',
message: sharedSuccessfully
? 'Ayat#$ayatNumber was shared.'
: 'Unable to share Ayat#$ayatNumber',
variant: sharedSuccessfully ? SnackbarType.success : SnackbarType.error,
);
}

void onSavePdfTap() {}
Future<void> _deletePDFFile(File file) async {
if (await file.exists()) {
await file.delete();
}
}

Future<void> onMakeNoteTap() async {
_setIsMakingNote(true);
await _showNoteDialog();
_setIsMakingNote(false);
}

Future<void> onSharePDFTap() async {
_setIsSavingPDF(true);
try {
final pdf = await _generateAyatPDF();
final file = await _savePDFToFile(pdf);
await _sharePDFFile(file);
await _deletePDFFile(file);
} catch (e) {
_snackbarService.showCustomSnackBar(
title: 'Error!',
message: e.toString(),
variant: SnackbarType.error,
);
}
_setIsSavingPDF(false);
}
}
3 changes: 2 additions & 1 deletion app/lib/global/widgets/horizontal_gap.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:quran_companion/global/services/size_helper_service.dart';

class HorizontalGap extends StatelessWidget {
final double width;
const HorizontalGap(this.width, {super.key});

@override
Widget build(BuildContext context) {
return SizedBox(width: width);
return SizedBox(width: width.w);
}
}
23 changes: 10 additions & 13 deletions app/lib/home/home_view.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/svg.dart';
import 'package:quran_companion/global/services/size_helper_service.dart';
import 'package:quran_companion/global/themes/colors.dart';
import 'package:quran_companion/global/themes/fonts.dart';
import 'package:quran_companion/global/utils/svg_constants.dart';
import 'package:quran_companion/global/widgets/custom_text.dart';
import 'package:quran_companion/global/widgets/horizontal_padding.dart';
import 'package:quran_companion/global/widgets/vertical_gap.dart';
Expand Down Expand Up @@ -40,20 +44,13 @@ class HomeView extends StatelessWidget {
],
),
const VerticalGap(13.5),
Container(
width: double.maxFinite,
height: 208.h,
decoration: BoxDecoration(
color: green,
Align(
alignment: Alignment.center,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.r),
),
child: const Center(
child: CustomText(
// TODO: Add home picture
'Picture to be added...',
size: 12,
font: poppinsRegular,
color: white,
child: SvgPicture.asset(
homeBanner,
width: MediaQuery.sizeOf(context).width.w - 63.w,
),
),
),
Expand Down
4 changes: 4 additions & 0 deletions app/linux/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#include "generated_plugin_registrant.h"

#include <url_launcher_linux/url_launcher_plugin.h>

void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}
1 change: 1 addition & 0 deletions app/linux/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
Expand Down
4 changes: 4 additions & 0 deletions app/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import FlutterMacOS
import Foundation

import package_info_plus
import path_provider_foundation
import share_plus
import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
Loading

0 comments on commit a019b22

Please sign in to comment.