Skip to content

Commit

Permalink
feat(app): Add add macOS app and enable support for dropping PkPasses…
Browse files Browse the repository at this point in the history
… into the macOS app
  • Loading branch information
ueman committed Jun 27, 2024
1 parent 0082f24 commit 3337adf
Show file tree
Hide file tree
Showing 40 changed files with 1,764 additions and 71 deletions.
29 changes: 7 additions & 22 deletions app/.metadata
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
# This file should be version controlled and should not be manually edited.

version:
revision: e285328a6909879584a27c9b8eec5fa3621ead93
channel: unknown
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
- platform: android
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
- platform: ios
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
- platform: linux
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: macos
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
- platform: web
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
- platform: windows
create_revision: e285328a6909879584a27c9b8eec5fa3621ead93
base_revision: e285328a6909879584a27c9b8eec5fa3621ead93
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49

# User provided section

Expand Down
93 changes: 56 additions & 37 deletions app/lib/home_page.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'package:app/db/database.dart';
import 'package:app/import_pass/import_page.dart';
import 'package:app/import_pass/pick_pass.dart';
import 'package:app/pass_backside/pass_backside_page.dart';
import 'package:app/router.dart';
import 'package:app/widgets/app_icon.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand Down Expand Up @@ -37,45 +39,62 @@ class _HomePageState extends State<HomePage> {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const AppIcon(),
centerTitle: true,
actions: [
IconButton(
onPressed: () => pickPass(context),
icon: const Icon(Icons.file_open),
),
IconButton(
onPressed: () => router.push('/settings'),
icon: const Icon(Icons.settings),
tooltip: AppLocalizations.of(context).settings,
return DropTarget(
onDragDone: (detail) async {
final firstFile = detail.files.firstOrNull;

if (firstFile == null) {
return;
}
// TODO(ueman): Add more validation

await router.push(
'/import',
extra: PkPassImportSource(
bytes: await detail.files.first.readAsBytes(),
),
],
),
body: passes.isEmpty
? const Center(child: Text('No passes'))
: RefreshIndicator(
onRefresh: () => loadPasses(),
child: ListView.builder(
itemCount: passes.length,
itemBuilder: (context, index) {
final pass = passes[index];
return Padding(
padding: const EdgeInsets.all(16.0),
child: InkWell(
child: PkPassWidget(pass: pass),
onTap: () {
router.push(
'/backside',
extra: PassBackSidePageArgs(pass, true),
);
},
),
);
},
),
);
},
child: Scaffold(
appBar: AppBar(
title: const AppIcon(),
centerTitle: true,
actions: [
IconButton(
onPressed: () => pickPass(context),
icon: const Icon(Icons.file_open),
),
IconButton(
onPressed: () => router.push('/settings'),
icon: const Icon(Icons.settings),
tooltip: AppLocalizations.of(context).settings,
),
],
),
body: passes.isEmpty
? const Center(child: Text('No passes'))
: RefreshIndicator(
onRefresh: () => loadPasses(),
child: ListView.builder(
itemCount: passes.length,
itemBuilder: (context, index) {
final pass = passes[index];
return Padding(
padding: const EdgeInsets.all(16.0),
child: InkWell(
child: PkPassWidget(pass: pass),
onTap: () {
router.push(
'/backside',
extra: PassBackSidePageArgs(pass, true),
);
},
),
);
},
),
),
),
);
}
}
27 changes: 22 additions & 5 deletions app/lib/import_pass/import_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@ import 'package:go_router/go_router.dart';
import 'package:passkit/passkit.dart';
import 'package:passkit_ui/passkit_ui.dart';

class PkPassImportSource {
PkPassImportSource({this.path, this.bytes})
: assert(path != null || bytes != null);

final String? path;
final List<int>? bytes;

Future<PkPass> getPass() async {
if (path != null) {
final Content content = await ContentResolver.resolveContent(path!);
return PkPass.fromBytes(content.data);
} else if (bytes != null) {
return PkPass.fromBytes(bytes!);
}
throw Exception('No data');
}
}

class ImportPassPage extends StatefulWidget {
const ImportPassPage({super.key, required this.path});
const ImportPassPage({super.key, required this.source});

final String path;
final PkPassImportSource source;

@override
State<ImportPassPage> createState() => _ImportPassPageState();
Expand All @@ -30,8 +48,7 @@ class _ImportPassPageState extends State<ImportPassPage> {
}

Future<void> _load() async {
final Content content = await ContentResolver.resolveContent(widget.path);
final pkPass = PkPass.fromBytes(content.data);
final pkPass = await widget.source.getPass();
setState(() {
pass = pkPass;
});
Expand All @@ -41,7 +58,7 @@ class _ImportPassPageState extends State<ImportPassPage> {
Widget build(BuildContext context) {
Widget child;
if (pass == null) {
child = Center(child: Text(widget.path));
child = const Center(child: CircularProgressIndicator());
} else {
child = Column(
children: [
Expand Down
3 changes: 2 additions & 1 deletion app/lib/import_pass/pick_pass.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:app/import_pass/import_page.dart';
import 'package:app/router.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/widgets.dart';
Expand Down Expand Up @@ -29,5 +30,5 @@ Future<void> pickPass(BuildContext context) async {
return;
}

await router.push('/import', extra: firstPath);
await router.push('/import', extra: PkPassImportSource(path: firstPath));
}
3 changes: 2 additions & 1 deletion app/lib/import_pass/receive_pass.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:app/import_pass/import_page.dart';
import 'package:app/router.dart';
import 'package:flutter/services.dart';
import 'package:receive_intent/receive_intent.dart';
Expand Down Expand Up @@ -46,5 +47,5 @@ Future<void> _onIntent(Intent? receivedIntent) async {
// TODO(ueman): show error popup?
return;
}
unawaited(router.push('/import', extra: path));
unawaited(router.push('/import', extra: PkPassImportSource(path: path)));
}
14 changes: 10 additions & 4 deletions app/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import 'dart:io';

import 'package:app/db/database.dart';
import 'package:app/db/preferences.dart';
import 'package:app/main_app.dart';
import 'package:app/import_pass/receive_pass.dart';
import 'package:app/main_app.dart';
import 'package:flutter/material.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDisplayMode.setHighRefreshRate();
if (Platform.isAndroid) {
await FlutterDisplayMode.setHighRefreshRate();
}
initDb();
await AppPreferences().init();
runApp(const MainApp());
await initReceiveIntent();
await initReceiveIntentWhileRunning();
if (Platform.isAndroid) {
await initReceiveIntent();
await initReceiveIntentWhileRunning();
}
}
4 changes: 4 additions & 0 deletions app/lib/pass_backside/pass_backside_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:developer';
import 'dart:io';
import 'dart:typed_data';

import 'package:app/pass_backside/app_metadata_tile.dart';
Expand Down Expand Up @@ -73,6 +74,9 @@ class _PassBacksidePageState extends State<PassBacksidePage> {
}

Future<void> _loadRelevantLocations() async {
if (!(Platform.isAndroid || Platform.isIOS)) {
return;
}
final locations = widget.pass.pass.locations;
if (locations == null || locations.isEmpty) {
return;
Expand Down
3 changes: 2 additions & 1 deletion app/lib/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ final router = GoRouter(
),
GoRoute(
path: '/import',
builder: (context, state) => ImportPassPage(path: state.extra as String),
builder: (context, state) =>
ImportPassPage(source: state.extra as PkPassImportSource),
),
GoRoute(
path: '/examples',
Expand Down
7 changes: 7 additions & 0 deletions app/macos/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Flutter-related
**/Flutter/ephemeral/
**/Pods/

# Xcode-related
**/dgph
**/xcuserdata/
2 changes: 2 additions & 0 deletions app/macos/Flutter/Flutter-Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
2 changes: 2 additions & 0 deletions app/macos/Flutter/Flutter-Release.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
32 changes: 32 additions & 0 deletions app/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Generated file. Do not edit.
//

import FlutterMacOS
import Foundation

import desktop_drop
import flutter_local_notifications
import geolocator_apple
import package_info_plus
import path_provider_foundation
import screen_brightness_macos
import share_plus
import shared_preferences_foundation
import sqlite3_flutter_libs
import url_launcher_macos
import wakelock_plus

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
}
43 changes: 43 additions & 0 deletions app/macos/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
platform :osx, '10.14'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_macos_podfile_setup

target 'Runner' do
use_frameworks!
use_modular_headers!

flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end
Loading

0 comments on commit 3337adf

Please sign in to comment.