Skip to content

Commit

Permalink
Paintroid-448 Flutter: Create Save/ Load functionality (#4)
Browse files Browse the repository at this point in the history
Paintroid 448: Implement Save / load functionality and refactoring of base architecture, added tests
  • Loading branch information
krispykalsi committed Aug 10, 2022
1 parent 10459b0 commit 5c648d5
Show file tree
Hide file tree
Showing 103 changed files with 3,826 additions and 424 deletions.
28 changes: 19 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,31 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
- uses: arduino/setup-protoc@v1
- uses: subosito/flutter-action@v2.5.0
with:
channel: 'stable'
cache: true
- name: Setup
run: |
flutter pub get
flutter pub run build_runner build
- name: Build release package
run: flutter build apk --release
- name: Archive build artifacts
uses: actions/upload-artifact@v3
with:
name: apk
path: build/app/outputs/flutter-apk/app-release.apk
dart pub global activate protoc_plugin
chmod +x generate_protos.sh
./generate_protos.sh
- name: Static Analysis
run: flutter analyze
- name: Unit Tests
run: flutter test test/unit
- name: Widget Tests
run: flutter test test/widget
run: flutter test test/widget
- name: Build release package
run: flutter build apk --release --split-per-abi
- name: Archive build artifacts
uses: actions/upload-artifact@v3
with:
name: apk
path: |
build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk
build/app/outputs/flutter-apk/app-x86_64-release.apk
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ migrate_working_dir/
/build/
coverage
*.mocks.dart
*.pb*.dart

# Web related
lib/generated_plugin_registrant.dart
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ For more information oriented towards developers please visit our [developers pa
## Getting Started

1. Install [Flutter](https://docs.flutter.dev/get-started/install)
2. Get dependencies - `flutter pub get`
3. Build supporting files - `flutter pub run build_runner build`
4. Run app - `flutter run lib/main.dart`
2. Set up the [Protocol Buffer](https://grpc.io/docs/languages/dart/quickstart/) compiler - `protoc` for Dart
4. Get dependencies - `flutter pub get`
5. Build supporting files - `./generate_files.sh`
6. Build protobuf files - `./generate_protos.sh`
7. Run app - `flutter run lib/main.dart`

## Tests

Expand Down
8 changes: 7 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
include: package:flutter_lints/flutter.yaml
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
missing_enum_constant_in_switch: error
exhaustive_cases: error
exclude:
- lib/**.pb*.dart
20 changes: 5 additions & 15 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,12 @@ if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 33
ndkVersion flutter.ndkVersion

compileOptions {
Expand All @@ -44,10 +34,10 @@ android {

defaultConfig {
applicationId "org.catrobat.paintroidflutter"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0.0"
}

buildTypes {
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.catrobat.paintroid">
<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.INTERNET" />
</manifest>
6 changes: 6 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.catrobat.paintroid">

<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,88 @@
package org.catrobat.paintroid

import android.Manifest
import android.content.ContentValues
import android.content.pm.PackageManager
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.NonNull
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.*
import java.io.IOException

class MainActivity: FlutterActivity() {
class MainActivity : FlutterActivity() {
private val hasWritePermission: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger, "org.catrobat.paintroid/photo_library"
).apply {
setMethodCallHandler { call, result ->
when (call.method) {
"saveToPhotos" -> {
if (!hasWritePermission) {
result.error(
"PERMISSION_DENIED",
"User explicitly denied WRITE_EXTERNAL_STORAGE permission",
null
)
return@setMethodCallHandler
}
val (filename, imageData) = extractImageData(call, result)
?: return@setMethodCallHandler
saveImageToPictures(filename, imageData)
result.success(null)
}
else -> result.notImplemented()
}
}
}
}

private fun saveImageToPictures(filename: String, data: ByteArray) {
val picturesUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, filename)
put(MediaStore.Images.Media.MIME_TYPE, "image/*")
}
contentResolver.insert(picturesUri, contentValues)?.also { uri ->
contentResolver.openOutputStream(uri)?.use { outputStream ->
outputStream.write(data)
} ?: throw IOException("Could not open output stream for uri: $uri")
} ?: throw IOException("Could not create image MediaStore entry")
}

private fun extractImageData(call: MethodCall, result: MethodChannel.Result): Pair<String, ByteArray>? {
val filename = call.argument<String>("fileName") ?: run {
result.error(
"INVALID_FILENAME",
"Image name is either not supplied or not of type String",
null
)
return null
}
val imageData = call.argument<ByteArray>("data") ?: run {
result.error(
"INVALID_IMAGE_DATA",
"Image data is either not supplied or not of type UInt8List",
null
)
return null
}
return Pair(filename, imageData)
}
}
1 change: 1 addition & 0 deletions generate_files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flutter pub run build_runner build --delete-conflicting-outputs
3 changes: 3 additions & 0 deletions generate_protos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cd lib/io/src/serialization/proto || exit
mkdir output
protoc --dart_out=output --proto_path=schema $(find schema -iname "*.proto") google/protobuf/any.proto
11 changes: 10 additions & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '9.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down Expand Up @@ -37,5 +37,14 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)

# https://pub.dev/packages/permission_handler
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_PHOTOS=1',
'PERMISSION_PHOTOS_ADD_ONLY=1'
]
end
end
end
73 changes: 72 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,22 +1,93 @@
PODS:
- DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.4)
- DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- image_picker_ios (0.0.1):
- Flutter
- integration_test (0.0.1):
- Flutter
- path_provider_ios (0.0.1):
- Flutter
- permission_handler_apple (9.0.4):
- Flutter
- SDWebImage (5.13.1):
- SDWebImage/Core (= 5.13.1)
- SDWebImage/Core (5.13.1)
- SwiftyGif (5.4.3)

DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)

SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- SDWebImage
- SwiftyGif

EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"

SPEC CHECKSUMS:
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
SDWebImage: fb26a455eeda4c7a55e4dcb6172dbb258af7a4ca
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780

PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
PODFILE CHECKSUM: 0bb9cd0668dcd7eee7f213e7cfebebde242de503

COCOAPODS: 1.11.3
Loading

0 comments on commit 5c648d5

Please sign in to comment.