diff --git a/README.md b/README.md index 2a58433..40e1e7b 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,17 @@ The power of AI in the palm of my hands! This is AI utility app for Android device, it basically uses AI model directly on-device to help on performing some tasks. +## Features +### Face Detection & Recognition + + +### Neural Style Transfer Current features: diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/NavigatorProvider.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/NavigatorProvider.java index 3a35763..f8437ad 100644 --- a/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/NavigatorProvider.java +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/NavigatorProvider.java @@ -18,6 +18,7 @@ import m.co.rh.id.a_jarwis.app.ui.page.HomePage; import m.co.rh.id.a_jarwis.app.ui.page.SplashPage; import m.co.rh.id.a_jarwis.app.ui.page.common.SelectFaceImagePage; +import m.co.rh.id.a_jarwis.app.ui.page.common.SelectNSTThemePage; import m.co.rh.id.a_jarwis.app.ui.page.common.ShowMessagePage; import m.co.rh.id.a_jarwis.base.constants.Routes; import m.co.rh.id.a_jarwis.settings.ui.page.SettingsPage; @@ -51,12 +52,13 @@ public INavigator getNavigator(Activity activity) { @SuppressLint("InflateParams") @SuppressWarnings("unchecked") - private Navigator setupMainActivityNavigator() { + private void setupMainActivityNavigator() { Map navMap = new ArrayMap<>(); navMap.put(Routes.SPLASH_PAGE, (args, activity) -> new SplashPage(Routes.HOME_PAGE)); navMap.put(Routes.HOME_PAGE, (args, activity) -> new HomePage()); navMap.put(Routes.SHOW_MESSAGE_PAGE, (args, activity) -> new ShowMessagePage()); navMap.put(Routes.SELECT_FACE_IMAGE_PAGE, (args, activity) -> new SelectFaceImagePage()); + navMap.put(Routes.SELECT_NST_THEME_PAGE, (args, activity) -> new SelectNSTThemePage()); navMap.put(Routes.SETTINGS_PAGE, (args, activity) -> new SettingsPage()); navMap.put(Routes.DONATIONS_PAGE, (args, activity) -> new DonationsPage()); navMap.putAll(mNavExtDialogConfig.getNavMap()); @@ -71,7 +73,6 @@ private Navigator setupMainActivityNavigator() { mActivityNavigatorMap.put(MainActivity.class, navigator); mApplication.registerActivityLifecycleCallbacks(navigator); mApplication.registerComponentCallbacks(navigator); - return navigator; } @Override diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/CommandProviderModule.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/CommandProviderModule.java index 091e75e..9a3c559 100644 --- a/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/CommandProviderModule.java +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/CommandProviderModule.java @@ -8,5 +8,6 @@ public class CommandProviderModule implements ProviderModule { @Override public void provides(ProviderRegistry providerRegistry, Provider provider) { providerRegistry.registerLazy(BlurFaceCommand.class, () -> new BlurFaceCommand(provider)); + providerRegistry.registerLazy(NSTApplyCommand.class, () -> new NSTApplyCommand(provider)); } } diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/NSTApplyCommand.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/NSTApplyCommand.java new file mode 100644 index 0000000..66439c4 --- /dev/null +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/provider/command/NSTApplyCommand.java @@ -0,0 +1,34 @@ +package m.co.rh.id.a_jarwis.app.provider.command; + +import android.net.Uri; + +import java.io.File; +import java.util.concurrent.ExecutorService; + +import io.reactivex.rxjava3.core.Single; +import m.co.rh.id.a_jarwis.base.provider.component.helper.FileHelper; +import m.co.rh.id.a_jarwis.ml_engine.provider.component.NSTEngine; +import m.co.rh.id.aprovider.Provider; + +public class NSTApplyCommand { + private final ExecutorService mExecutorService; + private final FileHelper mFileHelper; + private final NSTEngine mNSTEngine; + + public NSTApplyCommand(Provider provider) { + mExecutorService = provider.get(ExecutorService.class); + mFileHelper = provider.get(FileHelper.class); + mNSTEngine = provider.get(NSTEngine.class); + } + + public Single execute(Uri uriFile, int theme) { + return Single.fromFuture(mExecutorService.submit(() -> { + String fullPath = uriFile.getPath(); + int cut = fullPath.lastIndexOf("/"); + File result = mFileHelper + .createImageTempFile(fullPath.substring(cut + 1), uriFile); + mNSTEngine.enqueueNsT(result, theme); + return result; + })); + } +} diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/HomePage.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/HomePage.java index 4fc8323..b9f052d 100644 --- a/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/HomePage.java +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/HomePage.java @@ -26,9 +26,11 @@ import io.reactivex.rxjava3.schedulers.Schedulers; import m.co.rh.id.a_jarwis.R; import m.co.rh.id.a_jarwis.app.provider.command.BlurFaceCommand; +import m.co.rh.id.a_jarwis.app.provider.command.NSTApplyCommand; import m.co.rh.id.a_jarwis.app.ui.page.nav.param.FileList; import m.co.rh.id.a_jarwis.app.ui.page.nav.param.MessageText; import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedChoice; +import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedTheme; import m.co.rh.id.a_jarwis.base.constants.Routes; import m.co.rh.id.a_jarwis.base.provider.IStatefulViewProvider; import m.co.rh.id.a_jarwis.base.rx.RxDisposer; @@ -50,6 +52,7 @@ public class HomePage extends StatefulView implements RequireComponent private static final int REQUEST_CODE_IMAGE_AUTO_BLUR_FACE = 1; private static final int REQUEST_CODE_IMAGE_EXCLUDE_BLUR_FACE = 2; private static final int REQUEST_CODE_IMAGE_SELECTIVE_BLUR_FACE = 3; + private static final int REQUEST_CODE_IMAGE_NST_APPLY_PICTURE = 4; @NavInject private transient INavigator mNavigator; @@ -64,12 +67,14 @@ public class HomePage extends StatefulView implements RequireComponent private transient ExecutorService mExecutorService; private transient RxDisposer mRxDisposer; private transient BlurFaceCommand mBlurFaceCommand; + private transient NSTApplyCommand mNSTApplyCommand; // View related private transient DrawerLayout mDrawerLayout; private transient View.OnClickListener mOnNavigationClicked; private ArrayList mfacesList; + private SelectedTheme mSelectedNSTTheme; public HomePage() { mAppBarSV = new AppBarSV(); @@ -82,6 +87,7 @@ public void provideComponent(Provider provider) { mExecutorService = mSvProvider.get(ExecutorService.class); mRxDisposer = mSvProvider.get(RxDisposer.class); mBlurFaceCommand = mSvProvider.get(BlurFaceCommand.class); + mNSTApplyCommand = mSvProvider.get(NSTApplyCommand.class); mOnNavigationClicked = view -> { if (!mDrawerLayout.isOpen()) { mDrawerLayout.open(); @@ -109,6 +115,8 @@ protected View createView(Activity activity, ViewGroup container) { excludeBlurButton.setOnClickListener(this); Button selectiveBlurButton = rootLayout.findViewById(R.id.button_selective_blur_face); selectiveBlurButton.setOnClickListener(this); + Button nstApplyPictureButton = rootLayout.findViewById(R.id.button_nst_apply_picture); + nstApplyPictureButton.setOnClickListener(this); ViewGroup containerAppBar = rootLayout.findViewById(R.id.container_app_bar); containerAppBar.addView(mAppBarSV.buildView(activity, container)); return rootLayout; @@ -191,6 +199,40 @@ public void onClick(View view) { } else { startSelectiveAutoBlur(); } + } else if (id == R.id.button_nst_apply_picture) { + Activity activity = mNavigator.getActivity(); + if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_IMAGE_NST_APPLY_PICTURE); + } else { + startNstApplyPicture(); + } + } + } + + private void startNstApplyPicture() { + int title = m.co.rh.id.a_jarwis.base.R.string.title_what_to_do; + int body = m.co.rh.id.a_jarwis.base.R.string.pick_image_for_nst_apply_picture; + mNavigator.push(Routes.SHOW_MESSAGE_PAGE, new MessageText(title, body, true) + , (navigator, navRoute, activity1, currentView) -> + startNstApplyPicture_processFirstRespond(navRoute)); + } + + private void startNstApplyPicture_processFirstRespond(NavRoute navRoute) { + Serializable serializable = navRoute.getRouteResult(); + if (serializable instanceof SelectedChoice) { + int selectedChoice = ((SelectedChoice) serializable).getSelectedChoice(); + if (SelectedChoice.POSITIVE == selectedChoice) { + mNavigator.push(Routes.SELECT_NST_THEME_PAGE, (navigator, navRoute1, activity1, currentView) -> + startNstApplyPicture_processSecondRespond(activity1, navRoute1)); + } + } + } + + private void startNstApplyPicture_processSecondRespond(Activity activity, NavRoute navRoute) { + Serializable serializable = navRoute.getRouteResult(); + if (serializable instanceof SelectedTheme) { + mSelectedNSTTheme = (SelectedTheme) serializable; + pickImage(activity, REQUEST_CODE_IMAGE_NST_APPLY_PICTURE); } } @@ -363,6 +405,40 @@ public void onActivityResult(View currentView, Activity activity, INavigator INa .subscribeOn(Schedulers.from(mExecutorService)) .subscribe(consumeFile)); } + } else if (requestCode == REQUEST_CODE_IMAGE_NST_APPLY_PICTURE && resultCode == Activity.RESULT_OK) { + ClipData listPhoto = data.getClipData(); + Uri fullPhotoUri = data.getData(); + BiConsumer consumeFile = (file, throwable) -> { + if (throwable != null) { + mLogger.e(TAG, throwable.getMessage(), throwable); + } else { + mLogger.i(TAG, mSvProvider.getContext() + .getString(m.co.rh.id.a_jarwis.base.R.string.processing_, + file.getName())); + } + }; + if (listPhoto != null) { + int count = listPhoto.getItemCount(); + List uriList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + uriList.add(listPhoto.getItemAt(i).getUri()); + } + if (!uriList.isEmpty()) { + mRxDisposer.add("onActivityResult_nstApplyPicture_multiple", + Flowable.fromIterable(uriList) + .map(uri -> mNSTApplyCommand.execute(uri, mSelectedNSTTheme.getSelectedTheme()) + .blockingGet()) + .subscribeOn(Schedulers.from(mExecutorService)) + .doOnError(throwable -> consumeFile.accept(null, throwable)) + .subscribe(file -> consumeFile.accept(file, null)) + ); + } + } else { + mRxDisposer.add("onActivityResult_nstApplyPicture", + mNSTApplyCommand.execute(fullPhotoUri, mSelectedNSTTheme.getSelectedTheme()) + .subscribeOn(Schedulers.from(mExecutorService)) + .subscribe(consumeFile)); + } } } @@ -378,6 +454,16 @@ public void onRequestPermissionsResult(View currentView, Activity activity, INav grantResults[0] == PackageManager.PERMISSION_GRANTED ) { startExcludeAutoBlur(); + } else if (requestCode == REQUEST_CODE_IMAGE_SELECTIVE_BLUR_FACE + && grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED + ) { + startSelectiveAutoBlur(); + } else if (requestCode == REQUEST_CODE_IMAGE_NST_APPLY_PICTURE + && grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED + ) { + startNstApplyPicture(); } } } \ No newline at end of file diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/common/SelectNSTThemePage.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/common/SelectNSTThemePage.java new file mode 100644 index 0000000..caeb22b --- /dev/null +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/common/SelectNSTThemePage.java @@ -0,0 +1,54 @@ +package m.co.rh.id.a_jarwis.app.ui.page.common; + +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; + +import m.co.rh.id.a_jarwis.R; +import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedTheme; +import m.co.rh.id.a_jarwis.ml_engine.provider.component.NSTEngine; +import m.co.rh.id.anavigator.StatefulView; +import m.co.rh.id.anavigator.annotation.NavInject; +import m.co.rh.id.anavigator.component.INavigator; + +public class SelectNSTThemePage extends StatefulView implements View.OnClickListener { + + @NavInject + private transient INavigator mNavigator; + + @Override + protected View createView(Activity activity, ViewGroup container) { + View rootView = activity.getLayoutInflater().inflate(R.layout.page_select_nst_theme, container, false); + View mosaic = rootView.findViewById(R.id.container_mosaic_theme); + mosaic.setOnClickListener(this); + View candy = rootView.findViewById(R.id.container_candy_theme); + candy.setOnClickListener(this); + View rainPrincess = rootView.findViewById(R.id.container_rain_princess_theme); + rainPrincess.setOnClickListener(this); + View udnie = rootView.findViewById(R.id.container_udnie_theme); + udnie.setOnClickListener(this); + View pointilism = rootView.findViewById(R.id.container_pointilism_theme); + pointilism.setOnClickListener(this); + View backButton = rootView.findViewById(R.id.button_back); + backButton.setOnClickListener(this); + return rootView; + } + + @Override + public void onClick(View view) { + int id = view.getId(); + if (id == R.id.container_mosaic_theme) { + mNavigator.pop(new SelectedTheme(NSTEngine.THEME_MOSAIC)); + } else if (id == R.id.container_candy_theme) { + mNavigator.pop(new SelectedTheme(NSTEngine.THEME_CANDY)); + } else if (id == R.id.container_rain_princess_theme) { + mNavigator.pop(new SelectedTheme(NSTEngine.THEME_RAIN_PRINCESS)); + } else if (id == R.id.container_udnie_theme) { + mNavigator.pop(new SelectedTheme(NSTEngine.THEME_UDNIE)); + } else if (id == R.id.container_pointilism_theme) { + mNavigator.pop(new SelectedTheme(NSTEngine.THEME_POINTILISM)); + } else if (id == R.id.button_back) { + mNavigator.pop(); + } + } +} diff --git a/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/nav/param/SelectedTheme.java b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/nav/param/SelectedTheme.java new file mode 100644 index 0000000..b70505b --- /dev/null +++ b/app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/nav/param/SelectedTheme.java @@ -0,0 +1,15 @@ +package m.co.rh.id.a_jarwis.app.ui.page.nav.param; + +import java.io.Serializable; + +public class SelectedTheme implements Serializable { + private int selectedTheme; + + public SelectedTheme(int selectedTheme) { + this.selectedTheme = selectedTheme; + } + + public int getSelectedTheme() { + return selectedTheme; + } +} diff --git a/app/src/main/res/drawable/nst_theme_candy.jpg b/app/src/main/res/drawable/nst_theme_candy.jpg new file mode 100644 index 0000000..f40e5a3 Binary files /dev/null and b/app/src/main/res/drawable/nst_theme_candy.jpg differ diff --git a/app/src/main/res/drawable/nst_theme_mosaic.jpg b/app/src/main/res/drawable/nst_theme_mosaic.jpg new file mode 100644 index 0000000..63aa06f Binary files /dev/null and b/app/src/main/res/drawable/nst_theme_mosaic.jpg differ diff --git a/app/src/main/res/drawable/nst_theme_pointilism.jpg b/app/src/main/res/drawable/nst_theme_pointilism.jpg new file mode 100644 index 0000000..ac64b37 Binary files /dev/null and b/app/src/main/res/drawable/nst_theme_pointilism.jpg differ diff --git a/app/src/main/res/drawable/nst_theme_rain_princess.jpg b/app/src/main/res/drawable/nst_theme_rain_princess.jpg new file mode 100644 index 0000000..520f6a2 Binary files /dev/null and b/app/src/main/res/drawable/nst_theme_rain_princess.jpg differ diff --git a/app/src/main/res/drawable/nst_theme_udnie.jpg b/app/src/main/res/drawable/nst_theme_udnie.jpg new file mode 100644 index 0000000..3dbb29c Binary files /dev/null and b/app/src/main/res/drawable/nst_theme_udnie.jpg differ diff --git a/app/src/main/res/layout/page_home.xml b/app/src/main/res/layout/page_home.xml index 09f2e4f..07ec227 100644 --- a/app/src/main/res/layout/page_home.xml +++ b/app/src/main/res/layout/page_home.xml @@ -52,6 +52,17 @@ android:gravity="center" android:text="@string/title_selective_blur_face" app:icon="@drawable/ic_face_black" /> + + diff --git a/app/src/main/res/layout/page_select_nst_theme.xml b/app/src/main/res/layout/page_select_nst_theme.xml new file mode 100644 index 0000000..1df9fda --- /dev/null +++ b/app/src/main/res/layout/page_select_nst_theme.xml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/base/src/main/java/m/co/rh/id/a_jarwis/base/constants/Routes.java b/base/src/main/java/m/co/rh/id/a_jarwis/base/constants/Routes.java index 3e01ccc..e115abd 100644 --- a/base/src/main/java/m/co/rh/id/a_jarwis/base/constants/Routes.java +++ b/base/src/main/java/m/co/rh/id/a_jarwis/base/constants/Routes.java @@ -6,6 +6,7 @@ public class Routes { public static final String SHOW_MESSAGE_PAGE = "/common/showMessage"; public static final String SELECT_FACE_IMAGE_PAGE = "/common/selectFaceImage"; + public static final String SELECT_NST_THEME_PAGE = "/common/selectNstTheme"; public static final String SETTINGS_PAGE = "/settings"; public static final String DONATIONS_PAGE = "/donations"; diff --git a/base/src/main/java/m/co/rh/id/a_jarwis/base/provider/component/helper/FileHelper.java b/base/src/main/java/m/co/rh/id/a_jarwis/base/provider/component/helper/FileHelper.java index 168a78f..f266e91 100644 --- a/base/src/main/java/m/co/rh/id/a_jarwis/base/provider/component/helper/FileHelper.java +++ b/base/src/main/java/m/co/rh/id/a_jarwis/base/provider/component/helper/FileHelper.java @@ -30,10 +30,10 @@ public class FileHelper { private static final String TAG = FileHelper.class.getName(); - private Context mAppContext; - private ProviderValue mLogger; - private File mLogFile; - private File mTempFileRoot; + private final Context mAppContext; + private final ProviderValue mLogger; + private final File mLogFile; + private final File mTempFileRoot; public FileHelper(Provider provider) { mAppContext = provider.getContext().getApplicationContext(); @@ -122,15 +122,23 @@ public File getLogFile() { } public File createImageTempFile() throws IOException { + return createImageTempFile(UUID.randomUUID().toString()); + } + + public File createImageTempFile(String fileName) throws IOException { File parent = new File(mTempFileRoot, UUID.randomUUID().toString()); parent.mkdirs(); - File tmpFile = new File(parent, UUID.randomUUID().toString() + ".jpg"); + File tmpFile = new File(parent, fileName + ".jpg"); tmpFile.createNewFile(); return tmpFile; } public File createImageTempFile(Uri content) throws IOException { - File outFile = createImageTempFile(); + return createImageTempFile(UUID.randomUUID().toString(), content); + } + + public File createImageTempFile(String fileName, Uri content) throws IOException { + File outFile = createImageTempFile(fileName); try { copyImage(content, outFile); return outFile; diff --git a/base/src/main/res/drawable-anydpi/ic_palette_black.xml b/base/src/main/res/drawable-anydpi/ic_palette_black.xml new file mode 100644 index 0000000..577ea23 --- /dev/null +++ b/base/src/main/res/drawable-anydpi/ic_palette_black.xml @@ -0,0 +1,11 @@ + + + diff --git a/base/src/main/res/drawable-hdpi/ic_palette_black.png b/base/src/main/res/drawable-hdpi/ic_palette_black.png new file mode 100644 index 0000000..7b2ba90 Binary files /dev/null and b/base/src/main/res/drawable-hdpi/ic_palette_black.png differ diff --git a/base/src/main/res/drawable-mdpi/ic_palette_black.png b/base/src/main/res/drawable-mdpi/ic_palette_black.png new file mode 100644 index 0000000..2d5ed44 Binary files /dev/null and b/base/src/main/res/drawable-mdpi/ic_palette_black.png differ diff --git a/base/src/main/res/drawable-xhdpi/ic_palette_black.png b/base/src/main/res/drawable-xhdpi/ic_palette_black.png new file mode 100644 index 0000000..9dc51f2 Binary files /dev/null and b/base/src/main/res/drawable-xhdpi/ic_palette_black.png differ diff --git a/base/src/main/res/drawable-xxhdpi/ic_palette_black.png b/base/src/main/res/drawable-xxhdpi/ic_palette_black.png new file mode 100644 index 0000000..f7b5b85 Binary files /dev/null and b/base/src/main/res/drawable-xxhdpi/ic_palette_black.png differ diff --git a/base/src/main/res/values/colors.xml b/base/src/main/res/values/colors.xml index 9e1921c..190d1e4 100644 --- a/base/src/main/res/values/colors.xml +++ b/base/src/main/res/values/colors.xml @@ -7,6 +7,7 @@ #00E0E0 #E00070 #E07000 + #99616161 @color/white @color/gray_700 @color/black diff --git a/base/src/main/res/values/dimens.xml b/base/src/main/res/values/dimens.xml index d695c2b..fd90579 100644 --- a/base/src/main/res/values/dimens.xml +++ b/base/src/main/res/values/dimens.xml @@ -9,4 +9,5 @@ 18sp 6sp 50sp + 25sp \ No newline at end of file diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index c231300..c270437 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -9,17 +9,24 @@ Auto Blur Face Exclude Blur Face Selective Blur Face + Apply Neural Style Transfer to Picture Press BACK again to exit Buy developer a coffee Processing %s Done processing: %s - No image detected: %s + No face detected: %s Back Next Tap to select image What to do? Pick image with face(s) to be excluded from blur and then select image(s) from gallery that you want to blur the face(s) Pick image with face(s) to select the face(s) to be blurred and then select image(s) from gallery on which the face(s) to be blurred + Pick neural style transfer theme to be applied to image and then select image(s) from gallery on which image(s) to be applied on No face selected No Face + Mosaic + Candy + Rain Princess + Udnie + Pointilism \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png index a0f12cd..d7e76f0 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/FaceEngine.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/FaceEngine.java index a91d62c..b06a66a 100644 --- a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/FaceEngine.java +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/FaceEngine.java @@ -26,9 +26,9 @@ import m.co.rh.id.a_jarwis.base.provider.component.helper.FileHelper; import m.co.rh.id.a_jarwis.base.util.BitmapUtils; import m.co.rh.id.a_jarwis.base.util.SerializeUtils; -import m.co.rh.id.a_jarwis.ml_engine.workmanager.BlurFaceSerialFile; import m.co.rh.id.a_jarwis.ml_engine.workmanager.BlurFaceWorkRequest; import m.co.rh.id.a_jarwis.ml_engine.workmanager.Params; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.model.BlurFaceSerialFile; import m.co.rh.id.alogger.ILogger; import m.co.rh.id.aprovider.Provider; import m.co.rh.id.aprovider.ProviderValue; diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/NSTEngine.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/NSTEngine.java index dddf168..8595543 100644 --- a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/NSTEngine.java +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/provider/component/NSTEngine.java @@ -2,34 +2,107 @@ import android.graphics.Bitmap; +import androidx.work.Data; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import m.co.rh.id.a_jarwis.base.provider.component.helper.FileHelper; +import m.co.rh.id.a_jarwis.base.util.SerializeUtils; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.NSTApplyWorkRequest; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.Params; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.model.NSTApplySerialFile; +import m.co.rh.id.alogger.ILogger; import m.co.rh.id.aprovider.Provider; import m.co.rh.id.aprovider.ProviderValue; public class NSTEngine { + public static final int THEME_MOSAIC = 1; + public static final int THEME_CANDY = 2; + public static final int THEME_RAIN_PRINCESS = 3; + public static final int THEME_UDNIE = 4; + public static final int THEME_POINTILISM = 5; - private final ProviderValue mlEngine; + private static final String TAG = "NSTEngine"; + + private final WorkManager mWorkManager; + private final ILogger mLogger; + private final ProviderValue mMLEngine; + private final FileHelper mFileHelper; public NSTEngine(Provider provider) { - mlEngine = provider.lazyGet(MLEngineInstance.class); + mWorkManager = provider.get(WorkManager.class); + mLogger = provider.get(ILogger.class); + mMLEngine = provider.lazyGet(MLEngineInstance.class); + mFileHelper = provider.get(FileHelper.class); } public Bitmap applyMosaic(Bitmap bitmap) { - return mlEngine.get().getNSTMosaic().process(bitmap); + return mMLEngine.get().getNSTMosaic().process(bitmap); } public Bitmap applyCandy(Bitmap bitmap) { - return mlEngine.get().getNSTCandy().process(bitmap); + return mMLEngine.get().getNSTCandy().process(bitmap); } public Bitmap applyRainPrincess(Bitmap bitmap) { - return mlEngine.get().getNSTRainPrincess().process(bitmap); + return mMLEngine.get().getNSTRainPrincess().process(bitmap); } public Bitmap applyUdnie(Bitmap bitmap) { - return mlEngine.get().getNSTUdnie().process(bitmap); + return mMLEngine.get().getNSTUdnie().process(bitmap); } public Bitmap applyPointilism(Bitmap bitmap) { - return mlEngine.get().getNSTPointilism().process(bitmap); + return mMLEngine.get().getNSTPointilism().process(bitmap); + } + + public Bitmap apply(Bitmap bitmap, int theme) { + switch (theme) { + case THEME_MOSAIC: + return applyMosaic(bitmap); + case THEME_CANDY: + return applyCandy(bitmap); + case THEME_RAIN_PRINCESS: + return applyRainPrincess(bitmap); + case THEME_UDNIE: + return applyUdnie(bitmap); + case THEME_POINTILISM: + return applyPointilism(bitmap); + default: + return bitmap; + } + } + + public void enqueueNsT(File imageFile, int theme) { + ObjectOutputStream oos = null; + try { + File serialFile = mFileHelper.createTempFile(); + oos = new ObjectOutputStream(new FileOutputStream(serialFile)); + NSTApplySerialFile nstApplySerialFile = new NSTApplySerialFile(imageFile, theme); + oos.writeObject(nstApplySerialFile); + oos.close(); + Data.Builder inputBuilder = new Data.Builder(); + inputBuilder.putByteArray(Params.SERIAL_FILE, SerializeUtils.serialize(serialFile)); + OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(NSTApplyWorkRequest.class) + .setInputData(inputBuilder.build()) + .build(); + mWorkManager.enqueue(oneTimeWorkRequest); + } catch (Exception e) { + mLogger.e(TAG, e.getMessage(), e); + throw new RuntimeException(e); + } finally { + if (oos != null) { + try { + oos.close(); + } catch (IOException e) { + mLogger.e(TAG, e.getMessage(), e); + } + } + } } } diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceWorkRequest.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceWorkRequest.java index 7ee91c5..de0d259 100644 --- a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceWorkRequest.java +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceWorkRequest.java @@ -19,6 +19,7 @@ import m.co.rh.id.a_jarwis.base.provider.component.helper.MediaHelper; import m.co.rh.id.a_jarwis.base.util.SerializeUtils; import m.co.rh.id.a_jarwis.ml_engine.provider.component.FaceEngine; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.model.BlurFaceSerialFile; import m.co.rh.id.alogger.ILogger; import m.co.rh.id.aprovider.Provider; @@ -65,7 +66,7 @@ public Result doWork() { .getString(m.co.rh.id.a_jarwis.base.R.string.done_processing_, fileName)); } else { logger.i(TAG, getApplicationContext() - .getString(m.co.rh.id.a_jarwis.base.R.string.no_image_detected_, fileName)); + .getString(m.co.rh.id.a_jarwis.base.R.string.no_face_detected_, fileName)); } } catch (Exception e) { logger.e(TAG, e.getMessage(), e); diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/NSTApplyWorkRequest.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/NSTApplyWorkRequest.java new file mode 100644 index 0000000..e5d60b7 --- /dev/null +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/NSTApplyWorkRequest.java @@ -0,0 +1,78 @@ +package m.co.rh.id.a_jarwis.ml_engine.workmanager; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +import m.co.rh.id.a_jarwis.base.BaseApplication; +import m.co.rh.id.a_jarwis.base.provider.component.helper.MediaHelper; +import m.co.rh.id.a_jarwis.base.util.SerializeUtils; +import m.co.rh.id.a_jarwis.ml_engine.provider.component.NSTEngine; +import m.co.rh.id.a_jarwis.ml_engine.workmanager.model.NSTApplySerialFile; +import m.co.rh.id.alogger.ILogger; +import m.co.rh.id.aprovider.Provider; + +public class NSTApplyWorkRequest extends Worker { + private static final String TAG = "NSTApplyWorkRequest"; + + public NSTApplyWorkRequest(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @SuppressLint("MissingPermission") + @NonNull + @Override + public Result doWork() { + Provider provider = BaseApplication.of(getApplicationContext()).getProvider(); + ILogger logger = provider.get(ILogger.class); + MediaHelper mediaHelper = provider.get(MediaHelper.class); + NSTEngine nstEngine = provider.get(NSTEngine.class); + + File inputFile = null; + ObjectInputStream ois = null; + try { + byte[] serialFileB = getInputData().getByteArray(Params.SERIAL_FILE); + File serialFile = SerializeUtils.deserialize(serialFileB); + ois = new ObjectInputStream(new FileInputStream(serialFile)); + NSTApplySerialFile nstApplySerialFile = (NSTApplySerialFile) ois.readObject(); + inputFile = nstApplySerialFile.getInputFile(); + int theme = nstApplySerialFile.getTheme(); + + Bitmap input = BitmapFactory.decodeFile(inputFile.getAbsolutePath()); + Bitmap applied = nstEngine.apply(input, theme); + String fileName = inputFile.getName(); + if (applied != null) { + mediaHelper.insertImage(applied, fileName, fileName); + applied.recycle(); + input.recycle(); + logger.i(TAG, getApplicationContext() + .getString(m.co.rh.id.a_jarwis.base.R.string.done_processing_, fileName)); + } + } catch (Exception e) { + logger.e(TAG, e.getMessage(), e); + return Result.failure(); + } finally { + if (inputFile != null) { + inputFile.delete(); + } + if (ois != null) { + try { + ois.close(); + } catch (IOException e) { + // Leave blank + } + } + } + return Result.success(); + } +} diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceSerialFile.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/BlurFaceSerialFile.java similarity index 92% rename from ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceSerialFile.java rename to ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/BlurFaceSerialFile.java index d620af1..3ebfc27 100644 --- a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/BlurFaceSerialFile.java +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/BlurFaceSerialFile.java @@ -1,4 +1,4 @@ -package m.co.rh.id.a_jarwis.ml_engine.workmanager; +package m.co.rh.id.a_jarwis.ml_engine.workmanager.model; import java.io.File; import java.io.Serializable; diff --git a/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/NSTApplySerialFile.java b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/NSTApplySerialFile.java new file mode 100644 index 0000000..7204d4a --- /dev/null +++ b/ml-engine/src/main/java/m/co/rh/id/a_jarwis/ml_engine/workmanager/model/NSTApplySerialFile.java @@ -0,0 +1,23 @@ +package m.co.rh.id.a_jarwis.ml_engine.workmanager.model; + +import java.io.File; +import java.io.Serializable; + +public class NSTApplySerialFile implements Serializable { + private File inputFile; + private int theme; + + public NSTApplySerialFile(File inputFile, int theme) { + this.inputFile = inputFile; + this.theme = theme; + } + + public File getInputFile() { + return inputFile; + } + + public int getTheme() { + return theme; + } + +}