Obfuscate sensitive data in your pictures before sharing them online.

Overview

Privacy Blur

Logo

A cross-platform application to obfuscate sensitive data from images, targeting iOS and Android devices. Mainly written in dart with the Flutter SDK.

Table of contents

What does the app do?

The project is aiming to provide a free, clean and simple solution for users to manipulate image data. No in-app purchases. No ads. No watermark. No hassle. Free forever because privacy shouldn't cost anything. Free because we care!

Code style

We have some restrictions about the code style of this project. You can find all requirements in the CONTRIBUTING

Features

  • Blur / Pixelate effect
  • Fine / coarse grain effect
  • Round / Square area
  • Export to your camera roll

Flutter SDK Setup

The app is running on Flutter SDK 2.2 and dart 2.13. Before working with the project, please make sure to run flutter upgrade.

Windows

click here: Installation Guide on Windows

macOS

click here: Installation Guide on MacOS

Building

The app is targeted for iOS and Android on Phones and Tablets. Desktop and Web Platform may cause issues and are currently not planned.

It's recommended to run:

flutter clean
flutter pub get

as soon as building issues appear.

iOS-Building

Flutter guide for building on iOS

You will need an MacOS Machine to be able to run a Flutter iOS application. Please also make sure you installed the correct version of cocoapods and Xcode on your machine.

For deployment information visit: Deployment Guide iOS

Android-Building

Flutter guide for building on Android

No further requirements for building an android version.

For deployment information visit: Deployment Guide Android

Structure

lib/--+--main.dart (entry point)
      |
      +--resources/--- images, fonts, strings, etc...
      |
      +--------src/--+----- app.dart (some inital code)
                     +---router.dart (navigation handling)
                     +-------di.dart (dependency injection) 
                     |
                     |
                     +--screens/--screen_name/
                     |                  +--helpers/-- (garbage place, like many events and states)
                     |                  +--widgets/-- (internal widgets for this screen)
                     |                  |
                     |                  +--repo.dart
                     |                  +--bloc.dart
                     |                  +--view.dart                                  
                     |                 
                     |
                     +--widgets/-- (common widgets for application)
                     |
                     |
                     +----utils/-- (some utils for application if necessary)

Architecture

In order to build a maintainable, scalable and testable project, the app is build with an architectural pattern and file structure. For readability purposes we limited the file size to 300 lines.

Navigation

The navigation is kept relatively simple with Flutter Navigation v1. The router.dart file includes all routing and navigation logic in 2 classes.

  • ScreenNavigator: implements and provides methods to enable routing
  • AppRouter: declares all routes and their configuration

To be able to test each route individually the AppRouter has multiple constructors. A constructor must also be defined for each route. The constructor then overwrites what is the actual initial route and boots up at the specified location.

Each route receives the Dependency Injection instance, and the AppRouter instance itself. Optional arguments follow as 3rd parameter.

Dependency Injection

The Dependency Injection Class is used to inject all testable parts of the application in the right order. It holds the provider for different blocs and repositories. The instance of the class must be passed to the view, so that they can access all dependencies in the Provider/Consumer structure.

BLoC

In dart its useful to work with Streams, and the Provider pattern. That's why we chose the popular BLoC pattern. It makes sense to implement a bloc for each screen, but only if it needs to hold and manage data. Usually the bloc receives the corresponding Repositories from the Dependency Injection.

The BLoC class file is located in the same directory as the screen view. Each View is using events to trigger state updates inside the bloc, which are consumed by the view again. Events and states are located inside the helpers' directory on the screens' folder.

To be able to decide which state should be emitted, the bloc consumes events and handles the decision based on that. It's important to always yield something after each event, otherwise the UI won't be updated and blocked.

Stream<SampleState> mapEventToState(SampleEvent event) async* {
  if (event is EventOne) {
    yield* handleStateOneChange(event);
  } else if (event is EventTwo) {
    yield* handleStateTwoChange(event);
  } ...
}

The bloc works as the logic component of the screen and separates all logic from the UI. Thus, the bloc also shouldn't handle any UI parts.

Views

Each View represents the widget, that is used as screen in the Navigation route. It should only define the UI and consume/use state. Primarily it's better to implement screens as StatelessWidgets and only rerender the parts of the screen which really need to be rendered after state updates.

A Bloc-Screen is wrapped by a MultiBlocProvider, which gets the corresponding bloc from the Dependency Injection and makes it available through context for the nested BlocConsumer.

return MultiBlocProvider(
    providers: [_di.getSampleBloc()],
    child: BlocConsumer<SampleBloc, SampleState>(...)

On simple UI data changes as displaying a Toast Message we used the buildWhen condition of the bloc consumer to prevent a whole rerender. Only the MessageBar is drawn above the screen by the listener.

BlocConsumer<SampleBloc, SampleState>(
  listenWhen: (_, curState) => (curState is SimpleState),
  buildWhen: (_, curState) => !(curState is SimpleState),
  listener: (_, state) {
    showMessageOnSimpleState(...)
  }
  ...

Most parts of the views are separated in own methods. This improves readability and modularity. As soon the file size exceeds 300 lines or widgets grow to a huge size it makes sense to move the widgets them to another file.

Widgets

Widgets exist on common and screen scope level. Common Widgets are located in src/widgets and screen widgets in src/screen_name/widgets. Widgets on common level shouldn't implement screen specific configurations and should be made reusable with minimal setup. Widgets on screen scope are either only used in this place, or their usage is very specific to the screen.

Stateful Widgets

Best practise for Widgets which hold state is to also manage it internally. For example:

class WidgetState extends State<WidgetWithState> {
  late double _initialValue;

  @override
  void didUpdateWidget(covariant DeferredSlider oldWidget) {
    _initialValue = widget._initialValue;
    super.didUpdateWidget(oldWidget);
  }

  @override
  void initState() {
    _initialValue = widget._initialValue;
    super.initState();
  }
  
  ... Widget build ...
  
  void onChanged(doubleValue) {
    setState(() {
      _initialValue = doubleValue;
    });
    this.widget.onChanged(doubleValue);
  }
}

Adaptive Widgets

The app is targeted for iOS and Android, but needs to look different on each platform. That's why we split platform specific code (iOS/Android) inside src/widgets/adaptive_widgets_builder.dart. We saw, that Flutter is starting to build adaptive versions of Widgets. When they will complete with this, we can remove and replace "builders" inside this file with the adaptive version of Flutter widgets.

Internationalization

The app is translated into:

  • 🇺🇸 English (default language)
  • 🇩🇪 German

Translations live inside the lib/resources/i18n directory. To add new languages you need to do the following:

  • add lang code to supported languages
  var delegate = await LocalizationDelegate.create(
      ...
      supportedLocales: [..., '<language_code>']);
  • add json file for lang code in lib/resources/i18n/<lang_code>.json
  • copy structure from an existing .json file
  • As soon as new texts are added, the new keys need to be generated. Run: flutter flutter pub run build_runner build --delete-conflicting-outputs

Theme

The Theme is split into different platforms and additionally themeModes for android. theme_provider.dart

  • iosTheme (providing themeMode split iOS ThemeData)
  • light (providing light android ThemeData)
  • dark (providing dark android ThemeData)

Dark and Light mode are supported and depend on the system preference.

// AppBuilder widget
themeMode: ThemeMode.system,

Assets/Resources

All assets/resources can be found in lib/resouces. Images, icons and other common resources should be placed here.

Icons

Currently, the app is using a custom icon font which includes 4 icons for the image editing tools in the toolbar selection. Fonts can be generated here: fluttericon.com.

Do only include icons that are used inside the project!

Additional flutter provides default icons that can be used either from CupertinoIcons or Icons Class. To handle icons from one place the project has the icons_provider class. This is the place where one iconName can be defined and can have extra logic to split between platforms.

Images

Images can be added here. To handle different screen resolutions with image sizes there is the possibility to add an image in x2 and x3 sizes for the referring screens. On dart side its enough to load it like this: lib/resources/images/<image_name>. Dart handles the split between the resolutions.

Currently, the app only uses the App logo which is essential on Launch-/Splash and Main-Screen. Its also recommended limiting the number of static image files, due to the fact that they extend the project size by a large amount.

Strings

In this place all text strings that are used inside the application. It is planned to add i18n and i10n on a later stage of the project. All strings inside here need to be moved to the translation .aab files then.

ImageFilter library description

This library is a singleton. It uses very much memory while working, so its very bad idea to create few instances of this class.

Class can load and filter images.

Supported filters:

  • Pixelate
  • linear Blur (not Gaussian, too slow)

Class features:

  • preview of changes before commit
  • undo changes in some special area (erase tool)
  • processing squared and rounded areas
  • processing multiple areas in one transaction
  • overriding processed pixels all together with selected filter

How to work with this class:

   import 'dart:ui' as img_tools;                           
           final file = File(filename);  
           final imageFilter = ImageAppFilter();
           var completer = Completer<img_tools.Image>();
           img_tools.decodeImageFromList(file.readAsBytesSync(), (result) {
             completer.complete(result);
           });
           var image = await completer.future;
           
           /// VERY IMPORTANT TO USE AWAIT HERE!!!
           var filteredImage = await imageFilter.setImage(_blocState.image);
           imageFilter.transactionStart();
           imageFilter.setMatrix(MatrixAppPixelate(20));
           imageFilter.apply2CircleArea(200, 200, 150);
           /// for preview before saving (only changed area of image will be updated)
           filteredImage = await imageFilter.getImage();

           imageFilter.setMatrix(MatrixAppBlur(20));
           imageFilter.apply2CircleArea(400, 400, 250);

           /// save to cached image, after that you can not cancel changes
           /// only if you load image again.
           imageFilter.transactionCommit();

           /// get saved image after transaction completed (complete merged image without changed part)
           filteredImage = await imageFilter.getImage();

Methods

  • static void setMaxProcessedWidth(int) - default 1000 for blur speed optimization we need to know maximum processed area width
  • Future<ImageFilterResult> setImage(dart:ui.Image) - set image for filtering.
  • Future<ImageFilterResult> getImage() - if transactions is active, will return only changed area without background image. If transaction is closed - will return full updated images.
  • void transactionStart() - open transaction for changes
  • void transactionCancel() - reset changes and close transaction
  • void transactionCommit() - apply changes from transaction and close transaction
  • void setFilter(ImageAppMatrix newMatrix) - set current filter
  • void apply2SquareArea(int x1, int y1, double radius) - apply selected filter to square area with center and radius
  • void apply2CircleArea(int x1, int y1, double radius) - apply selected filter to circle area with center and radius
  • void cancelCurrent() - cancel all current changes in active transaction
  • void cancelSquare(int, int, int, int) - cancel changes in square area
  • void cancelCircle(int, int, int) - cancel changes in circle area

Filters

  • MatrixAppPixelate(int blockSize)
  • MatrixAppBlur(int blockSize) - Linear Blur

Testing

run flutter test command in project root or test folder. All test-files must be ended with _test.dart suffix

Dependencies

Do only add null-safety dependencies! The application runs in sound null safety. Consequently, all dependencies added need to follow this restriction.

  • flutter_styled_toast: used for alerts
  • image_size_getter: get image sizes to decide if image should be rotated
  • flutter_exif_rotation: used for initial rotation on image load
  • url_launcher: used to open a link in main screen
  • image_picker: used to get the access to the image library
  • image: used for image file handling
  • image_gallery_saver: used to save image to library
  • permission_handler: used for accessing OS permission status
  • path_provider: used to read OS image paths
  • bloc: library for architectural pattern class
  • flutter_bloc: flutter additional features for dart bloc library
  • mockito: used for mocks in testing
  • bloc_test: used for testing blocs
  • flutter_translate: adding i18n to the app
  • flutter_translate_gen: used to generate variables for static usage of translations

License

MIT

Comments
  • Free-form rectangle area selection

    Free-form rectangle area selection

    Hello

    I just found your app through FDroid. Great concept, but I find the UI a bit hard to use.

    One point specifically that would help a lot is freeform rectangle selection. Right now, pixelating an email address requires many small rectangles to be placed. It'd be much easier if I could just drag and select.

    Thanks for your contribution to the open source ecosystem!

    opened by keunes 2
  • Use underscore rather than dash in entry point

    Use underscore rather than dash in entry point

    The filename convention seems to have changed from dash to underscore: main_full.dart main_foss.dart The screenshot could be updated too as it shows dashes.

    opened by nicolas-raoul 0
  • Add build flavors

    Add build flavors

    Description

    This Pull Request aims to add flavor building functionality to the application. In favor of inlcuding different dependencies in different stores the app is uploaded to, the building process needs to be separated by flavors.

    opened by pirminb97 0
  • localize library usage description

    localize library usage description

    Description

    When accessing the photo library a dialog is prompted to grant access to it. This dialog must be localized, becuase it could cause AppStore deployment rejections

    opened by pirminb97 0
  • adding macos support

    adding macos support

    Description

    This PR aims to enable desktop support (especially macOS) for the PrivacyBlur Mobile Application.

    Requirements

    • [x] permission handling (Apple Developer Entitlements)
    • [x] file picking
    • [ ] ~face detection~ (preliminary deactivated on desktop)
    • [x] filter apply
    • [x] file saving
    • [x] desktop window boundaries
    • [x] store assets
    opened by pirminb97 0
  • Migration to independent platform for provision of source code

    Migration to independent platform for provision of source code

    A. Problem / Goal

    Since the purchase of GitHub by Microsoft in 2018, a dependence on the BigTech corporation can no longer be denied.

    On the one hand, I can understand why GitHub was chosen as the platform for making source code available: "Everyone is here".

    On the other hand, I see the danger of a vendor-lockin effect and that open source projects become centrally dependent on Microsoft. In my eyes, this is very dangerous for free and open source software and hardware projects.

    In the medium and long term, the goal would be to become independent of GitHub and thus of Microsoft. The Gitea-based Codeberg project of Codeberg e. V. in Berlin would be a good choice here.

    There are also (legal) problems with compliance with the licence of GitHub functions, such as the co-pilot.

    B. Solution

    My considered solution to the problem described in A. would be the following:

    1. A user of this open source project creates a user account on https://codeberg.org/
    2. If necessary: This user creates an organisation for the project.
    3. A "personal access token" is created on the GitHub account, which has appropriate rights to the organisation repositories, using the developer options in the settings.
    4. all repositories would be migrated with this access token into the ownership of the organisation created in step two.

    Regarding step four, there is an entry in the documentation of Codeberg: https://docs.codeberg.org/advanced/migrating-repos/

    C. Alternatives

    A possible alternative would be to perform the first three steps as described in B. A possible alternative would be to perform the first three steps as described in B., and modify the fourth step to include a mirror of GitHub. So that all issues and such that would be created in the GitHub repository would be transferred to the Codeberg repository.

    D. Responsibilities

    I would see the responsibility in the owners of the repository and, if necessary, additional project participants.

    E. Other

    Basically, a look at the documentation of Codeberg is not unwise: https://docs.codeberg.org

    Should it be necessary to manage repositories in organisations, this is also possible under Codeberg, see: https://docs.codeberg.org/collaborating/create-organization/

    Regarding licensing there is a page in the documentation of Codeberg: https://docs.codeberg.org/getting-started/licensing/

    F. Risk

    Last but not least, it must be assumed that people could potentially create fewer issues because it is a new platform and it is less known. It remains to be seen how and when the principle of decentralisation or federation will be implemented in Gitea, on which Codeberg, the GitHub alternative, is based, see the following article: https://social.exozy.me/@ta180m/108631221939677386

    opened by Lukas2112 0
  • Loss of picture quality, even well outside of blurred areas

    Loss of picture quality, even well outside of blurred areas

    I took a picture, and want to upload it onto a Wikipedia article. Before uploading it, I need to erase one passer-by's tiny face on the right. PrivacyBlur seems perfect for that task: Screenshot_20211208-210919 I press Save. Now only the tiny part I blurred (and the few blocks that surround it) should have changed, right? Unfortunately, comparing with the original, we can see that the actually most pixels have changed color, only the low-frequency sky has a few remaining original pixels: 57afbe10f1d2c59527c9f96cae105c99 vRAJa4hMe1x1ftNQ3oeTknkwhcFUOgvr The original picture for reference (GitHub might modify it so feel free to email me asking for the original): dense4

    JPEG supports several lossless operations, for instance cropping can be lossless if made along the blocks. Changing a few blocks (8x8 or 16x16) around a face should trigger losses only in these blocks.

    Some might consider this nitpicking but:

    • Not losing quality is extremely important for my purpose.
    • The quality loss is visible to the naked eye.
    • The goal of PrivacyBlur is to do one thing only and do it well. :-)

    By the way here are the most egregious changes, as you can see some areas have changed almost as much as the blurred face: 57afbe10f1d2c59527c9f96cae105c99 Pbe2uisZj93nVVs1X7Be8HrmhTuTXT6Q

    opened by nicolas-raoul 3
  • App does not show full picture width at start. Risk of not blurring people at the edges

    App does not show full picture width at start. Risk of not blurring people at the edges

    When opening a picture, the left and right parts of the picture are not shown at first. A user could blur the faces and think that the picture is now safe, even though they missed a face at the left or right. This is a privacy risk. How about showing the full width of the picture when opening it?

    opened by nicolas-raoul 1
  • [Feature request]: Add the ability to NOT remove EXIF metadata

    [Feature request]: Add the ability to NOT remove EXIF metadata

    There are many situations where one wants to blur faces but not remove EXIF metadata:

    • Passer-by on a picture meant to upload to Wikipedia. Wikimedia Commons needs to know with what kind of camera the picture was taken, and other pieces of metadata.
    • People in the background of a picture that I want to post to social media or to a family's Google Photos. The picture's exact time and location is important so that it appears at the correct time in the timeline, and at the correct place on the pictures map. Metadata helps services group pictures in a smart way, it is weird that among 50 pictures only the picture where I blurred a passer-by's face is split as a different group.

    I know you want to keep the UI simple, but thanks a lot for your consideration! :-)

    opened by nicolas-raoul 2
E-Studying-V1 - Flutter application where you can download files from an api and unarchive them and open them and see their contents

E-Studying-V1 - Flutter application where you can download files from an api and unarchive them and open them and see their contents

Chakib Ammar Aouchiche 0 Jan 20, 2022
Run asynchronous code before building your widget

Loader Sometimes you need to load some data before building your widget. Because initState doesn't support asynchronous loading you need to find anoth

Ali Ghanbari 4 Apr 17, 2022
Instagram is a free, online photo-sharing application and social network platform

Instagram is a free, online photo-sharing application and social network platform that was acquired by Facebook in 2012. Instagram allows users to edit and upload photos and short videos through a mobile app.

Behruz Hurramov 4 Dec 6, 2022
BankGit helps you manage your account and transactions more efficiently by breaking your account into branches for various purposes and then making transactions directly from them.

Bank Git Web Hosted Here : https://bank-management-45848.web.app/ Bank Git is an application built with Flutter and Firebase to help you manage your b

Yash Johri 27 Dec 26, 2022
Music reader for online data (Google Drive implementation). Cross platform goal : iOS, Android, MacOS, Windows

omusic A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if this i

JeanNicolasdeLamballerie 0 Nov 30, 2021
A multiplatform Dart movie app with 40% of code sharing between Flutter and the Web.

A multiplatform Dart movie app with 40% of code sharing between Flutter and the Web.

Iiro Krankka 3.4k Dec 30, 2022
Platform to post/say something without sharing personal information.

Anon is an Open Source Application where it's users will be able to share their thoughts without their identity being revealed i.e Anonymous. When the

Ismael Shakverdiev 18 Sep 13, 2022
Sharik is an open-source, cross-platform solution for sharing files via Wi-Fi or Mobile Hotspot

Share files across devices with Sharik! It works with Wi-Fi connection or Tethering (Wi-Fi Hotspot). No internet connection needed. Contributing Feel

Mark Motliuk 844 Jan 1, 2023
A live location sharing app built on Flutter and Firebase as backend

DISCLAIMER I want to stress on three main points: This was my first Flutter project so I have not used any best practices and it needs a lot of code r

ManojNB 237 Dec 30, 2022
Sharing What I Love To Do

How to create release apk? flutter build apk --target-platform android-arm,android-arm64 --split-per-abi //normal flutter build apk --target-platform

Rajesh 58 Jan 5, 2023
The expense sharing app.

Tabs The expense sharing app -- made with Flutter, Firebase and ❤️ . Keep track of what your friends owe you for that pizza you agreed to split; or re

Michael Roudnitski 280 Nov 23, 2022
ShareACab: App for sharing cab with college students

ShareACab An app for sharing cab with college students Description Usually, after exams or when the mid-sem break begins, there is a large surge of pe

DevClub IIT Delhi 67 Nov 23, 2022
An app for sharing polls in an interactive and anonymous way

Stay Home Polls App An app for sharing surveys in an interactive and anonymous way, mainly on sociocultural issues produced by the period of confineme

Albert Mañosa 6 Nov 21, 2022
DoubtBin is a doubt solving and resources sharing portfolio.

DoubtBin is a doubt solving and resources sharing portfolio. It includes implemenation of concepts of Object Oriented Programming, Front-end Development and Back-end Development.

null 17 Oct 14, 2022
Venni client app - A flutter ride-sharing end-user app supporting map location picking

Venni client app - A flutter ride-sharing end-user app supporting map location picking, driver location tracking, in-app credit card payments, trip rating system, and trip history.

Abrantes 1 Jan 3, 2022
Flutter plugin to run all creme sharing routines.

Creme Share plugin A Flutter plugin to share content from your Flutter app to social apps. Platform Support Android (WIP) iOS ❌ ✔️ Usage To use this p

Creme 10 Dec 20, 2022
A Flutter application for Muslims that help them challenge and motivate themselves and their friends to read Azkar in a fun way.

A Flutter application for Muslims that help them challenge and motivate themselves and their friends to read Azkar in a fun way.

null 33 Oct 30, 2022
Let me go is a mobile application which drivers can be notified when an Ambulance is near to them in a traffic.

Let Me Go Let Me Go is a mobile application which was build in order to notify vehicle drivers when an ambulance is near to them within a specific dis

Shakya Peiris 1 Dec 19, 2021
This pub lets you share any kind of files (csv, mp4, png etc), take screenshot of the widgets you want and return as Image and share them directly as well in the form of an image.

share_files_and_screenshot_widgets This pub lets you share any kind of files (csv, mp4, png etc), take screenshot of the widgets you want and return a

Jay Mehta 22 Dec 28, 2022