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

Issues
  • 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
  • upgrade flutter 2.2

    upgrade flutter 2.2

    opened by pirminb97 1
  • pix-154

    pix-154

    opened by LeonidArefev 1
  • Hot fix Image cache

    Hot fix Image cache

    opened by LeonidArefev 1
  • Cancel filter bug

    Cancel filter bug

    opened by LeonidArefev 1
  • Override confirmation dialog

    Override confirmation dialog

    opened by LeonidArefev 1
  • Preview Mode Button

    Preview Mode Button

    opened by pirminb97 1
  • Fastlane Setup

    Fastlane Setup

    null

    opened by pirminb97 1
  • i18n: Add zh-TW

    i18n: Add zh-TW

    Summary

    This PR adds Taiwan, Chinese (Traditional) (zh-TW) to its i18n strings.

    opened by Still34 1
  • Adding contents to the site

    Adding contents to the site

    opened by pirminb97 1
  • 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
  • [Feature request]: From Android gallery, share a picture to PrivacyBlur, then share again to target app

    [Feature request]: From Android gallery, share a picture to PrivacyBlur, then share again to target app

    I often browse my recent pictures using Android's stock gallery, and think "Oh I want to share that picture to Facebook!". I believe it is most people's workflow.

    It would be great if I could just tap "Share to", select PrivacyBlur, configure the blurring, then tap "Share to" and select Facebook.

    This avoids losing time finding that picture. Two times. First finding it within PrivacyBlur, then finding it within the Facebook app. Since I already have the picture open under my eyes within the gallery app, I should never be asked to find it.

    In addition to costing time, requiring the user to find the picture causes a risk to choose the wrong picture:

    • At the first step, not choosing the best picture to share because Android's picker only shows small square thumbnails and does not allow to zoom to see details. For instance if you have 10 out-of-focus pictures and 1 good one, in the Android picker it is really hard to guess which picture is the good one.
    • At the second step, mistakenly not choosing the blurred version. Huge privacy risk.

    All social apps support the "share to" intent. All privacy apps I have tried support the "share to" intent (examples: Point Blur, Scrambled Exif, LLCrop, Photo Editor, etc)

    Thanks, keep up the great work! :-)

    Screenshot_20211207-122710

    opened by nicolas-raoul 1
  • More efficient screen space use

    More efficient screen space use

    Thanks again for this app :)

    The options when creating a blur take quite some (vertical) screenspace. Snapseed (by Google) does this quite a bit more efficiently.

    Concretely, I would:

    • Move the 'preview' button to the top bar
    • Deceease the margin around the settings (e.g. the size slider)
    opened by keunes 0
  • [Feature request]: Saving current selected filter and size

    [Feature request]: Saving current selected filter and size

    After starting the application, I want to see by default the same filter type and filter strength that I used last time.

    opened by LeonidArefev 0
  • MacOS AppBar changes

    MacOS AppBar changes

    null

    opened by pirminb97 0
  • AI resistant blurring

    AI resistant blurring

    This is no news for security people. Blurring faces still carries information. There were research articles years ago. I just did a quick search and found this: https://today.duke.edu/2020/06/artificial-intelligence-makes-blurry-faces-look-more-60-times-sharper. And of course quite recent is this one: https://www.vice.com/en/article/xgdq87/deepfakes-japan-arrest-japanese-porn

    I have not checked what the state of the art blurring is but when I did some research 2016 there were basically two options:

    • Black rectangle over face.
    • Place another face over the one you are about to blur, then blur it.

    I just had an idea for another option. Random pixel colors for blurring.

    Would be great if you could look into this. I found your app on F-Droid. The only other relevant app seems to be https://guardianproject.info/apps/obscuracam/. They support black rectangle, I guess for that reason.

    opened by nxxxse 3
  • [Feature request]: add the ability to delete the exif metadata and compress the image

    [Feature request]: add the ability to delete the exif metadata and compress the image

    I installed the app from f-droid today and found it very nice and functional, plus it's very easy to use and intuitive. It's a shame that the f-droid version doesn't have automatic facial recognition, however: great job!

    I think adding the ability to delete the exif metadata and compress the image would become a great all in one solution for sharing photos and images.

    opened by darhma 3
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 19 Jan 12, 2022
A Simple Todo app design in Flutter to keep track of your task on daily basis. Its build on BLoC Pattern. You can add a project, labels, and due-date to your task also you can sort your task on the basis of project, label, and dates

WhatTodo Life can feel overwhelming. But it doesn’t have to. A Simple To-do app design in flutter to keep track of your task on daily basis. You can a

Burhanuddin Rashid 912 Jan 9, 2022
GChat is a chatting application developed using Flutter(Dart) and firebase for 2 users. Trying to Develop an application that does not sell your data with whatsapp rolling out its privacy policy updates.

Gchat - The Chatting Application A Flutter project for chatting. I used Android Studio and you can you any editor of your choice for ex: VS Code, Inte

Sanchaksh Kaul 5 Aug 17, 2021
Movie Lib is a mobile application where you can find the movies of your interest. This app provides a collection of movies of different languages according to your interest.

Movie Lib Movie Lib is a mobile application where you can find the movies of your interest. This app provides a collection of movies of different lang

Abhijith Kp 6 Sep 28, 2021
A simple easy to use Flutter DApp , which keeps a track of all your day to day transactions by using Ethereum blockchain in the background which in turn increases your credit score.

Sahayog A simple easy to use Flutter DApp , which keeps a track of all your day to day transactions by using Ethereum blockchain in the background whi

Utkarsh Agarwal 14 Jan 9, 2022
News App created in Flutter using News API for fetching realtime data and Firebase as the backend and authenticator.

News Buzz News App created in Flutter using News API for fetching realtime data and Firebase as the backend and authenticator. Features Custom news fe

Ankur Kedia 506 Dec 11, 2021
An open source encrypted peer-to-peer system. Own data, own privacy.

An open source encrypted peer-to-peer system. Own data, own privacy.

Cymple Tech 390 Jan 12, 2022
Indonesia Corona data viewer using flutter

corona_app A new Flutter project. Getting Started Clone this project git clone https://github.com/athallahmaajid/Indonesia-Corona-App.git Development

null 3 Jul 10, 2021
An android app built using flutter that displays and forecast the specific city Weather and Climate for dynamic time event by collecting the data from API that is provided for free by OPENWEATHER site.

clima_weather_reporter A new Flutter application. Getting Started This project is a starting point for a Flutter application. A few resources to get y

dev_allauddin 4 Nov 17, 2021
The FlexGrid control provides a powerful and quickly way to display data in a tabular format. It is including that frozened column/row,loading more, high performance and better experience in TabBarView/PageView.

flex_grid Language: English| 中文简体 The FlexGrid control provides a powerful and quickly way to display data in a tabular format. It is including that f

FlutterCandies 32 Jan 6, 2022
A digital BookShelf for your reading progress.

BookSearch a flutter medium series What is this? An open source app which revolves all around books: “Collecting books as you read them, virtually”. I

Norbert Kozsir 474 Dec 19, 2021
A personal app to track and manage your expenses.

?? Batua ?? A Personal App to track and manage your expenses! Batua is the best money manager and daily expense tracker to automatically and securely

Pratyush M 39 Jan 9, 2022
Your friendly neighborhood application.

Welcome to Aas Pass ?? Inspiration Time and again we have observed an on-the-spot need for borrowing daily devices, enquiring about essential services

Satwik Dudeja 3 May 30, 2021
A package that lets you include a cool, nice looking and validated Password TextFormField in your app to enhance user experience. The package is fully & easily modifiable.

A package that lets you include a cool, nice looking and validated Password TextFormField in your app to enhance user experience. The package is fully

Muhammad Hamza 18 Jan 15, 2022
Easy to use open source Hub 🕸️ to control your smart devices from one app.

CyBear Jinni Hub Welcome! This repository is in charge of controlling smart devices and is part of the CyBear Jinni Smart Home system. The software is

CyBear Jinni 19 Jan 18, 2022
Easy to use open source Hub 🕸️ to control your smart devices from one app.

CyBear Jinni Hub Welcome! This repository is in charge of controlling smart devices and is part of the CyBear Jinni Smart Home system. The software is

CyBear Jinni 13 Jul 22, 2021
A Flutter application to easily manage and control your Algorand nodes.

Algorand Node Companion App Algorand Node Companion App is a mobile, web and desktop application that can manage, operate and track the status of your

Tomas Verhelst 14 Dec 30, 2021