Skip to content

Commit

Permalink
feat: linux platform support (#174)
Browse files Browse the repository at this point in the history
- notifications
- build
- build documentation

---------

Co-authored-by: Miroslav Mazel <enjoyingfoss+git@pm.me>
Co-authored-by: Fries_I23 <FriesI23@outlook.com>
  • Loading branch information
3 people authored May 28, 2024
1 parent 7ec19b9 commit 72b0cd8
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ migration:
- platform: root
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
- platform: linux
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
- platform: windows
create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ multiple versions on your local machine, or simply use this submodule to build i
2. open an android or ios emulator
3. run `flutter run --debug`

Building for Linux requires installing the following packages for the [SQFlite database](https://pub.dev/packages/sqflite_common_ffi#linux):

```sudo apt-get -y install libsqlite3-0 libsqlite3-dev```

## Contributing

I am an independent developer and do not have professional expertise in writing
Expand Down
2 changes: 1 addition & 1 deletion lib/provider/app_reminder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class AppReminderViewModel extends ChangeNotifier
Future<bool> processAppReminder(L10n? l10n) async {
final reminder = this.reminder;
if (reminder.enabled && l10n != null) {
if (await NotificationService().requestPermissions() != true) {
if (await NotificationService().requestPermissions() == false) {
return false;
}
switch (reminder.type) {
Expand Down
48 changes: 46 additions & 2 deletions lib/reminders/notification_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -172,40 +172,84 @@ class NotificationIosChannelData
interruptionLevel: InterruptionLevel.passive);
}

class NotificationLinuxChannelData
implements NotificationChannelDataABC<LinuxNotificationDetails> {
final LinuxNotificationDetails? _debug;
final LinuxNotificationDetails? _habitReminder;
final LinuxNotificationDetails? _appReminder;
final LinuxNotificationDetails? _appDebugger;

const NotificationLinuxChannelData({
LinuxNotificationDetails? debug,
LinuxNotificationDetails? habitReminder,
LinuxNotificationDetails? appReminder,
LinuxNotificationDetails? appDebugger,
}) : _debug = debug,
_habitReminder = habitReminder,
_appReminder = appReminder,
_appDebugger = appDebugger;

@override
LinuxNotificationDetails get debug =>
_debug ?? const LinuxNotificationDetails();

@override
LinuxNotificationDetails get habitReminder =>
_habitReminder ?? const LinuxNotificationDetails();

@override
LinuxNotificationDetails get appReminder =>
_appReminder ?? const LinuxNotificationDetails();

@override
LinuxNotificationDetails get appDebugger =>
_appDebugger ?? const LinuxNotificationDetails();
}

class NotificationChannelData extends ChangeNotifier
implements NotificationChannelDataABC<NotificationDetails> {
final NotificationAndroidChannelData _androidChannel;
final NotificationIosChannelData _iosChannel;
final NotificationLinuxChannelData _linuxChannel;

NotificationChannelData({
NotificationAndroidChannelData? androidChannel,
NotificationIosChannelData? iosChannel,
NotificationLinuxChannelData? linuxChannel,
}) : _androidChannel =
androidChannel ?? const NotificationAndroidChannelData(),
_iosChannel = iosChannel ?? const NotificationIosChannelData();
_iosChannel = iosChannel ?? const NotificationIosChannelData(),
_linuxChannel = linuxChannel ?? const NotificationLinuxChannelData();

@override
NotificationDetails get debug => NotificationDetails(
android: _androidChannel.debug,
iOS: _iosChannel.debug,
linux: _linuxChannel.debug,
macOS: _iosChannel.debug,
);

@override
NotificationDetails get habitReminder => NotificationDetails(
android: _androidChannel.habitReminder, iOS: _iosChannel.habitReminder);
android: _androidChannel.habitReminder,
iOS: _iosChannel.habitReminder,
linux: _linuxChannel.habitReminder,
macOS: _iosChannel.debug,
);

@override
NotificationDetails get appReminder => NotificationDetails(
android: _androidChannel.appReminder,
iOS: _iosChannel.appReminder,
linux: _linuxChannel.appReminder,
macOS: _iosChannel.appReminder,
);

@override
NotificationDetails get appDebugger => NotificationDetails(
android: _androidChannel.appDebugger,
iOS: _iosChannel.appDebugger,
linux: _linuxChannel.appReminder,
macOS: _iosChannel.appDebugger,
);
}
36 changes: 36 additions & 0 deletions lib/reminders/notification_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ abstract interface class NotificationService implements AsyncInitialization {
factory NotificationService() {
if (_instance != null) return _instance!;
if (Platform.isWindows) return _instance = const FakeNotificationService();
if (Platform.isLinux) return _instance = const LinuxNotificationService();
return _instance = const NotificationServiceImpl();
}
}
Expand Down Expand Up @@ -131,11 +132,16 @@ final class NotificationServiceImpl implements NotificationService {
],
);

// linux setting
const linuxSettings =
LinuxInitializationSettings(defaultActionName: "Open notification");

// combine settings
final initializationSettings = InitializationSettings(
android: androidSettings,
iOS: darwinSettings,
macOS: darwinSettings,
linux: linuxSettings,
);

await plugin.initialize(
Expand Down Expand Up @@ -355,6 +361,36 @@ final class NotificationServiceImpl implements NotificationService {
}
}

final class LinuxNotificationService extends NotificationServiceImpl {
const LinuxNotificationService();

//TODO: Lame implementation; plugin doesn't support scheduling on Linux,
// need to find some solutions.
@override
Future<bool> regrAppReminderInDaily(
{required String title,
required String subtitle,
required TimeOfDay timeOfDay,
required NotificationDetails details,
Duration? timeout = NotificationServiceImpl.defaultTimeout}) =>
Future.value(false);

//TODO: Lame implementation; plugin doesn't support scheduling on Linux,
// need to find some solutions.
@override
Future<bool> regrHabitReminder<T>(
{required DBID id,
required HabitUUID uuid,
required String name,
String? quest,
required HabitReminder reminder,
required HabitDate? lastUntrackDate,
required NotificationDetails details,
DateTime? crtDate,
Duration? timeout = NotificationServiceImpl.defaultTimeout}) =>
Future.value(false);
}

final class FakeNotificationService implements NotificationService {
const FakeNotificationService();

Expand Down
1 change: 1 addition & 0 deletions linux/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flutter/ephemeral
139 changes: 139 additions & 0 deletions linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "mhabit")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "io.github.friesi23.mhabit")

# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)

# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()

# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()

# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()

# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})

# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)


# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)


# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()

# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)

set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")

install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)

foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)

# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
Loading

0 comments on commit 72b0cd8

Please sign in to comment.