diff options
Diffstat (limited to 'Software/Android_Studio')
47 files changed, 1726 insertions, 60 deletions
diff --git a/Software/Android_Studio/ColorCapture/app/build.gradle b/Software/Android_Studio/ColorCapture/app/build.gradle index febf35bea..6caa47dec 100644 --- a/Software/Android_Studio/ColorCapture/app/build.gradle +++ b/Software/Android_Studio/ColorCapture/app/build.gradle @@ -39,7 +39,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:support-v4:27.1.1' @@ -47,7 +47,6 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'io.reactivex.rxjava2:rxjava:2.1.5' compile 'com.google.dagger:dagger:2.11' @@ -59,8 +58,8 @@ dependencies { compile 'com.jakewharton:butterknife:8.7.0' compile 'net.danlew:android.joda:2.9.9.1' compile 'com.squareup:otto:1.3.8' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0' annotationProcessor 'com.google.dagger:dagger-android-processor:2.11' annotationProcessor 'com.google.dagger:dagger-compiler:2.11' + implementation project(':onboarding') } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/color_fine_tuning-web.png b/Software/Android_Studio/ColorCapture/app/src/main/color_fine_tuning-web.png Binary files differnew file mode 100644 index 000000000..7f1c46806 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/color_fine_tuning-web.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/cpp/native-lib.cpp b/Software/Android_Studio/ColorCapture/app/src/main/cpp/native-lib.cpp index 6da71fd81..3afccbec0 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/cpp/native-lib.cpp +++ b/Software/Android_Studio/ColorCapture/app/src/main/cpp/native-lib.cpp @@ -3,18 +3,132 @@ #include <opencv2/core.hpp> #include <opencv2/imgproc/imgproc_c.h> #include <ColorCaptureLib.h> +#include <BhBlocks.h> #include <android/log.h> using namespace cv; -extern "C" JNIEXPORT jstring JNICALL -Java_com_twine_colorcapture_views_main_MainActivity_stringFromJNI( - JNIEnv *env, - jobject /* this */) -{ - ColorCaptureLib capture; - Mat rgb; +Mat *mCanny = NULL; + +enum RotateFlags { + ROTATE_90DEG_CLOCKWISE = 0, //Rotate 90 degrees clockwise + ROTATE_180DEG = 1, //Rotate 180 degrees clockwise + ROTATE_90_DEFCOUNTERCLOCKWISEDEG = 2, //Rotate 270 degrees clockwise +}; - std::string hello = "Hello from C++"; - return env->NewStringUTF(hello.c_str()); +void rot90(cv::Mat &matImage, int rotflag) { + //1=CW, 2=CCW, 3=180 + if (rotflag == 1) { + transpose(matImage, matImage); + flip(matImage, matImage, 1); //transpose+flip(1)=CW + } else if (rotflag == 2) { + transpose(matImage, matImage); + flip(matImage, matImage, 0); //transpose+flip(0)=CCW + } else if (rotflag == 3) { + flip(matImage, matImage, -1); //flip(-1)=180 + } else if (rotflag != 0) { //if not 0,1,2,3: + cout << "Unknown rotation flag(" << rotflag << ")" << endl; + } } + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_twine_colorcapture_opencv_ImageProcessor_ProcessImage( + JNIEnv *env, jobject instance, jint width, + jint height, jbyteArray NV21FrameData, + jintArray outPixels,jintArray wrapedOutPixels) +{ + jbyte *pNV21FrameData = env->GetByteArrayElements(NV21FrameData, 0); + jint *poutPixels = env->GetIntArrayElements(outPixels, 0); + jint *pwrapedPixels = env->GetIntArrayElements(wrapedOutPixels, 0); + + //// + + jboolean has_result = false; + + try { + ColorCaptureLib capture; + + if (mCanny == NULL) { + mCanny = new Mat(height, width, CV_8UC1); + } + + Mat yuv(height + height / 2, width, CV_8UC1, (unsigned char *) pNV21FrameData); + Mat rgb; + Mat result(width, height, CV_8UC4, (unsigned char *) poutPixels); + cvtColor(yuv, rgb, COLOR_YUV2RGB_NV21); + + Mat gray; + Mat dst; + cvtColor(rgb, gray, CV_RGB2GRAY); + cv::Laplacian(gray, dst, CV_64F); + + cv::Scalar mu, sigma; + cv::meanStdDev(dst, mu, sigma); + + double focusMeasure = sigma.val[0] * sigma.val[0]; + + __android_log_print(ANDROID_LOG_ERROR, "FOCUS", "\n Focus measure is %f \n", focusMeasure); + + rot90(rgb,ROTATE_180DEG); + + //resize(src, dst, dst.size(), 0, 0, interpolation); + +/* Point2f src_center(rgb.cols / 2.0F, rgb.rows / 2.0F); + Mat rot_mat = getRotationMatrix2D(src_center, -90, 1.0); + warpAffine(rgb, rgb, rot_mat, rgb.size());*/ + + vector<Point> vertices = capture.GetQRVertices4(rgb); + + for (size_t i = 0; i < vertices.size(); i++) { + circle(rgb, vertices[i], 2, CV_RGB(0, 0, 255), -1); + } + + int w = 300; + int h = 330; + + Mat wraped(h,w,CV_8UC4, (unsigned char *) pwrapedPixels); + + if (vertices.size() == 4) { + + has_result = jboolean(true); + + Mat m = capture.ApplyHomography(rgb, vertices, Size(w, h)); + cvtColor(m, wraped, COLOR_RGB2BGRA); + //BhBlocks b(image, w / columns, h / rows); + //b.drawBlocks(image, Scalar(0, 0, 0), 1); + } + + cvtColor(rgb, result, COLOR_RGB2BGRA); + +/* vector<Point> vertices = capture.GetQRVertices4(image); + + for (size_t i = 0; i < vertices.size(); i++) + { + circle(image, vertices[i], 2, CV_RGB(0, 0, 255), -1); + } + + int w = 330; + int h = 300; + int columns = 11; + int rows = 10; + + if (vertices.size() == 4) + { + capture.ApplyHomography(image, vertices, Size(w, h)); + BhBlocks b(image, w / columns, h / rows); + b.drawBlocks(image, Scalar(0, 0, 0), 1); + }*/ + + + jsize size = env->GetArrayLength(outPixels); + + env->ReleaseByteArrayElements(NV21FrameData, pNV21FrameData, 0); + env->ReleaseIntArrayElements(outPixels, poutPixels, 0); + env->ReleaseIntArrayElements(wrapedOutPixels, pwrapedPixels, 0); + } + catch (Exception ex) + { + env->ThrowNew(env->FindClass("java/lang/NullPointerException"), ex.what()); + } + return has_result; +}
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ApplicationComponent.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ApplicationComponent.java index 1173c8751..b3a790039 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ApplicationComponent.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ApplicationComponent.java @@ -1,5 +1,6 @@ package com.twine.colorcapture.dagger; +import com.twine.colorcapture.views.capture.CaptureFragment; import com.twine.colorcapture.views.home.HomeFragment; import com.twine.colorcapture.views.loading.LoadingFragment; import com.twine.colorcapture.views.main.MainActivity; @@ -23,6 +24,8 @@ public interface ApplicationComponent void inject(LoadingFragment view); void inject(HomeFragment view); + + void inject(CaptureFragment view); MainActivityVM provideMainActivityVM(); } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ViewModelsModule.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ViewModelsModule.java index 41786b2a5..ff2e83a61 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ViewModelsModule.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ViewModelsModule.java @@ -3,6 +3,7 @@ package com.twine.colorcapture.dagger; import com.squareup.otto.Bus; import com.twine.colorcapture.navigation.INavigationProvider; import com.twine.colorcapture.notification.INotificationProvider; +import com.twine.colorcapture.views.capture.CaptureFragmentVM; import com.twine.colorcapture.views.home.HomeFragmentVM; import com.twine.colorcapture.views.loading.LoadingFragmentVM; import com.twine.colorcapture.views.main.MainActivityVM; @@ -38,6 +39,13 @@ public class ViewModelsModule @Singleton public HomeFragmentVM provideHomeFragmentVM(Bus eventBus, INotificationProvider notificationProvider, INavigationProvider navigationProvider) { - return new HomeFragmentVM(); + return new HomeFragmentVM(navigationProvider); + } + + @Provides + @Singleton + public CaptureFragmentVM provideCaptureFragmentVM(Bus eventBus, INotificationProvider notificationProvider, INavigationProvider navigationProvider) + { + return new CaptureFragmentVM(); } } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/FragmentBase.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/FragmentBase.java index 850dc57db..d9cb58725 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/FragmentBase.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/FragmentBase.java @@ -4,6 +4,7 @@ import android.app.Fragment; import android.databinding.DataBindingUtil; import android.databinding.ViewDataBinding; import android.os.Bundle; +import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -68,6 +69,14 @@ public abstract class FragmentBase<BindingView extends ViewDataBinding, VM exten onCreateListener = null; } + + new Handler().postDelayed(() -> + { + + onCreated(); + + },100); + return binding.getRoot(); } catch (Exception e) { @@ -148,4 +157,9 @@ public abstract class FragmentBase<BindingView extends ViewDataBinding, VM exten * @return the title */ public abstract String getTitle(); + + protected void onCreated() + { + + } } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/RelayCommand.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/RelayCommand.java index cda96d88e..9574c864f 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/RelayCommand.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/RelayCommand.java @@ -47,7 +47,7 @@ public class RelayCommand * @param view the view * @param command the command */ - @BindingAdapter("android:command") + @BindingAdapter("bind:command") public static void CommandBinding(View view, RelayCommand command) { command.bindedView = view; diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/navigation/NavigationView.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/navigation/NavigationView.java index ed9cce00f..d336d1930 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/navigation/NavigationView.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/navigation/NavigationView.java @@ -4,4 +4,5 @@ public enum NavigationView { Loading, Home, + Capture, } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/opencv/ImageProcessor.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/opencv/ImageProcessor.java new file mode 100644 index 000000000..02dde50e7 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/opencv/ImageProcessor.java @@ -0,0 +1,6 @@ +package com.twine.colorcapture.opencv; + +public class ImageProcessor +{ + public native boolean ProcessImage(int width, int height, byte[] NV21FrameData, int[] pixels,int[] wpixels); +} diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragment.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragment.java new file mode 100644 index 000000000..d068a2291 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragment.java @@ -0,0 +1,249 @@ +package com.twine.colorcapture.views.capture; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.ImageFormat; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; +import android.hardware.Camera.Size; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.Toast; + +import com.twine.colorcapture.App; +import com.twine.colorcapture.R; +import com.twine.colorcapture.databinding.FragmentCaptureBinding; +import com.twine.colorcapture.mvvm.FragmentBase; +import com.twine.colorcapture.opencv.ImageProcessor; + +import java.io.IOException; +import java.util.List; + +@SuppressWarnings("ALL") +public class CaptureFragment extends FragmentBase<FragmentCaptureBinding, CaptureFragmentVM> implements ICaptureFragment, SurfaceHolder.Callback, Camera.PreviewCallback +{ + private Camera camera = null; + private ImageView imagePreview = null; + private ImageView imagewrappedPreview = null; + private Bitmap bitmap = null; + private Bitmap wrappedBitmap = null; + private int[] pixels = null; + private int[] wrappedPixels = null; + private byte[] frameData = null; + private int imageFormat; + private boolean bProcessing = false; + private int cameraId; + private int previewWidth; + private int previewHeight; + private ImageProcessor processor; + private Handler mHandler; + private SurfaceView surfaceView; + + public CaptureFragment() + { + // Required empty public constructor + mHandler = new Handler(Looper.getMainLooper()); + processor = new ImageProcessor(); + } + + @Override + protected void onCreated() + { + super.onCreated(); + + imagePreview = getView().findViewById(R.id.imagePreview); + imagewrappedPreview = getView().findViewById(R.id.imagePreviewWrapped); + + surfaceView = new SurfaceView(this.getActivity()); + + SurfaceHolder camHolder = surfaceView.getHolder(); + camHolder.addCallback(this); + camHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + + FrameLayout surfraceFrame = (FrameLayout) getView().findViewById(R.id.surfaceViewFrame); + surfraceFrame.addView(surfaceView, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + } + + @Override + protected int getLayoutId() + { + return R.layout.fragment_capture; + } + + @Override + protected void inject() + { + App.getComponent().inject(this); + } + + @Override + public String getTitle() + { + return "Capture"; + } + + @Override + public void onPreviewFrame(byte[] data, Camera camera) + { + if (imageFormat == ImageFormat.NV21) + { + //We only accept the NV21(YUV420) format. + if (!bProcessing) + { + frameData = data; + mHandler.post(DoImageProcessing); + } + } + } + + @Override + public void surfaceCreated(SurfaceHolder surfaceHolder) + { + int numberOfCameras = Camera.getNumberOfCameras(); + for (int i = 0; i < numberOfCameras; i++) + { + CameraInfo info = new CameraInfo(); + Camera.getCameraInfo(i, info); + if (info.facing == CameraInfo.CAMERA_FACING_BACK) + { + cameraId = i; + break; + } + } + + camera = Camera.open(cameraId); + + try + { + camera.setPreviewDisplay(surfaceHolder); + camera.setPreviewCallback(this); + } + catch (IOException e) + { + camera.release(); + camera = null; + } + } + + @Override + public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) + { + Camera.Parameters parameters; + + setCameraDisplayOrientation(); + + parameters = camera.getParameters(); + + List<Size> sizes = parameters.getSupportedPreviewSizes(); + + for (Camera.Size size : parameters.getSupportedPreviewSizes()) + { + if (size.width >= 1200 & size.width <= 1280) + { + parameters.setPreviewSize(1280, 720); + parameters.setPictureSize(1280, 720); + break; + } + } + + int width = parameters.getPreviewSize().width; + int height = parameters.getPreviewSize().height; + + imageFormat = parameters.getPreviewFormat(); + + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + + camera.setParameters(parameters); + + parameters = camera.getParameters(); + + previewWidth = parameters.getPreviewSize().width; + previewHeight = parameters.getPreviewSize().height; + + bitmap = Bitmap.createBitmap(previewHeight, previewWidth, Bitmap.Config.ARGB_8888); + wrappedBitmap = Bitmap.createBitmap(300, 330, Bitmap.Config.ARGB_8888); + + pixels = new int[previewWidth * previewHeight]; + wrappedPixels = new int[330 * 300]; + + camera.startPreview(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder surfaceHolder) + { + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.release(); + camera = null; + } + + private void setCameraDisplayOrientation() + { + android.hardware.Camera.CameraInfo info = + new android.hardware.Camera.CameraInfo(); + android.hardware.Camera.getCameraInfo(cameraId, info); + int rotation = this.getActivity().getWindowManager().getDefaultDisplay().getRotation(); + int degrees = 0; + switch (rotation) + { + case Surface.ROTATION_0: + degrees = 0; + break; + case Surface.ROTATION_90: + degrees = 90; + break; + case Surface.ROTATION_180: + degrees = 180; + break; + case Surface.ROTATION_270: + degrees = 270; + break; + } + + int result; + if (info.facing == CameraInfo.CAMERA_FACING_FRONT) + { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; // compensate the mirror + } + else + { // back-facing + result = (info.orientation - degrees + 360) % 360; + } + camera.setDisplayOrientation(result); + + } + + private Runnable DoImageProcessing = new Runnable() + { + public void run() + { + bProcessing = true; + processor.ProcessImage(previewWidth, previewHeight, frameData, pixels, wrappedPixels); + + bitmap.setPixels(pixels, 0, previewHeight, 0, 0, previewHeight, previewWidth); + imagePreview.setImageBitmap(bitmap); + + wrappedBitmap.setPixels(wrappedPixels, 0, 300, 0, 0, 300, 330); + imagewrappedPreview.setImageBitmap(wrappedBitmap); + + bProcessing = false; + } + }; +}
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragmentVM.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragmentVM.java new file mode 100644 index 000000000..6be978036 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragmentVM.java @@ -0,0 +1,8 @@ +package com.twine.colorcapture.views.capture; + +import com.twine.colorcapture.mvvm.ViewModelBase; + +public class CaptureFragmentVM extends ViewModelBase<ICaptureFragment> +{ + +} diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/ICaptureFragment.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/ICaptureFragment.java new file mode 100644 index 000000000..c88d632b6 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/ICaptureFragment.java @@ -0,0 +1,7 @@ +package com.twine.colorcapture.views.capture; + +import com.twine.colorcapture.mvvm.IView; + +public interface ICaptureFragment extends IView +{ +} diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragment.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragment.java index d0294533e..76bdfe20f 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragment.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragment.java @@ -1,11 +1,20 @@ package com.twine.colorcapture.views.home; +import android.Manifest; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + import com.twine.colorcapture.App; import com.twine.colorcapture.R; +import com.twine.colorcapture.core.IAction1; import com.twine.colorcapture.databinding.FragmentHomeBinding; import com.twine.colorcapture.mvvm.FragmentBase; public class HomeFragment extends FragmentBase<FragmentHomeBinding, HomeFragmentVM> implements IHomeFragment { + private IAction1<Boolean> cameraAccessAction; public HomeFragment() { @@ -29,4 +38,38 @@ public class HomeFragment extends FragmentBase<FragmentHomeBinding, HomeFragment { return "Home"; } + + @Override + public void requestCameraAccess(IAction1<Boolean> action) + { + cameraAccessAction = action; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + if (ContextCompat.checkSelfPermission(this.getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this.getActivity(), + new String[]{Manifest.permission.CAMERA}, + 1); + } + else + { + cameraAccessAction.invoke(true); + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + switch (requestCode) { + case 1: { + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + cameraAccessAction.invoke(true); + } else { + cameraAccessAction.invoke(false); + } + } + } + } } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragmentVM.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragmentVM.java index 048ea5265..d7b8e3ba7 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragmentVM.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragmentVM.java @@ -1,15 +1,31 @@ package com.twine.colorcapture.views.home; +import com.twine.colorcapture.mvvm.RelayCommand; import com.twine.colorcapture.mvvm.ViewModelBase; +import com.twine.colorcapture.navigation.INavigationProvider; +import com.twine.colorcapture.navigation.NavigationView; import javax.inject.Inject; public class HomeFragmentVM extends ViewModelBase<IHomeFragment> { + private INavigationProvider navigationProvider; + public RelayCommand startCaptureCommand; @Inject - public HomeFragmentVM() + public HomeFragmentVM(INavigationProvider navigationProvider) { + this.navigationProvider = navigationProvider; + startCaptureCommand = new RelayCommand(() -> + { + view.requestCameraAccess((granted) -> + { + if (granted) + { + this.navigationProvider.navigateTo(NavigationView.Capture, true); + } + }); + }); } } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/IHomeFragment.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/IHomeFragment.java index e4f94c04b..17f94c7db 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/IHomeFragment.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/IHomeFragment.java @@ -1,8 +1,10 @@ package com.twine.colorcapture.views.home; +import com.twine.colorcapture.core.IAction; +import com.twine.colorcapture.core.IAction1; import com.twine.colorcapture.mvvm.IView; public interface IHomeFragment extends IView { - + void requestCameraAccess(IAction1<Boolean> action); } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivity.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivity.java index 9cebd1526..6d8e4d5ca 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivity.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivity.java @@ -1,7 +1,7 @@ package com.twine.colorcapture.views.main; +import android.app.FragmentTransaction; import android.graphics.Color; -import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; @@ -12,6 +12,8 @@ import com.twine.colorcapture.databinding.ActivityMainBinding; import com.twine.colorcapture.mvvm.ActivityBase; import com.twine.colorcapture.navigation.INavigationProvider; import com.twine.colorcapture.navigation.NavigationView; +import com.twine.onboarding.PaperOnboardingFragment; +import com.twine.onboarding.PaperOnboardingPage; import java.util.ArrayList; @@ -25,6 +27,7 @@ public class MainActivity extends ActivityBase<ActivityMainBinding, MainActivity // Used to load the 'native-lib' library on application startup. static { + System.loadLibrary("opencv_java3"); System.loadLibrary("native-lib"); } @@ -37,31 +40,36 @@ public class MainActivity extends ActivityBase<ActivityMainBinding, MainActivity // Example of a call to a native method //TextView tv = (TextView) findViewById(R.id.sample_text); //tv.setText(stringFromJNI()); - -/* PaperOnboardingPage scr1 = new PaperOnboardingPage("Hotels", + + PaperOnboardingPage scr1 = new PaperOnboardingPage("Welcome to true color capture", "All hotels and hostels are sorted by hospitality rating", - Color.parseColor("#678FB4"), R.drawable.onboarding_pager_round_icon, R.drawable.onboarding_pager_round_icon); + Color.parseColor("#678FB4"), R.drawable.hand_fabric, R.drawable.onboarding_pager_round_icon); PaperOnboardingPage scr2 = new PaperOnboardingPage("Banks", "We carefully verify all banks before add them into the app", - Color.parseColor("#65B0B4"), R.drawable.onboarding_pager_round_icon, R.drawable.onboarding_pager_round_icon); + Color.parseColor("#65B0B4"), R.drawable.hand_phone, R.drawable.onboarding_pager_round_icon); PaperOnboardingPage scr3 = new PaperOnboardingPage("Stores", "All local stores are categorized for your convenience", - Color.parseColor("#9B90BC"), R.drawable.onboarding_pager_round_icon, R.drawable.onboarding_pager_round_icon); - + Color.parseColor("#9B90BC"), R.drawable.take_picture, R.drawable.onboarding_pager_round_icon); + ArrayList<PaperOnboardingPage> elements = new ArrayList<>(); elements.add(scr1); elements.add(scr2); elements.add(scr3); + + PaperOnboardingFragment onboardingFragment = PaperOnboardingFragment.newInstance(elements); + + onboardingFragment.setOnRightOutListener(() -> + { - PaperOnboardingFragment onboardingFragment = PaperOnboardingFragment.newInstance(elements);*/ - -/* FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction(); + navigationProvider.navigateTo(NavigationView.Loading, false); + + }); + + FragmentTransaction fragmentTransaction = this.getFragmentManager().beginTransaction(); fragmentTransaction.add(R.id.fragment_container, onboardingFragment); - fragmentTransaction.commit();*/ + fragmentTransaction.commit(); navigationProvider.registerNavigationActivity(this, R.id.fragment_container, "com.twine.colorcapture.views"); - - navigationProvider.navigateTo(NavigationView.Loading, false); } @Override diff --git a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivityVM.java b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivityVM.java index e5af8ccb6..dc08f701c 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivityVM.java +++ b/Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivityVM.java @@ -1,17 +1,22 @@ package com.twine.colorcapture.views.main; import com.squareup.otto.Bus; +import com.twine.colorcapture.mvvm.DependencyProperty; +import com.twine.colorcapture.mvvm.RelayCommand; import com.twine.colorcapture.mvvm.ViewModelBase; import com.twine.colorcapture.navigation.INavigationProvider; +import com.twine.colorcapture.navigation.NavigationView; import com.twine.colorcapture.notification.INotificationProvider; import javax.inject.Inject; public class MainActivityVM extends ViewModelBase<IMainActivity> { + private INavigationProvider navigationProvider; + @Inject public MainActivityVM(Bus eventBus, INotificationProvider notificationProvider, INavigationProvider navigationProvider) { - + this.navigationProvider = navigationProvider; } } diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/application_logs.png b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/application_logs.png Binary files differnew file mode 100644 index 000000000..584379dc8 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/application_logs.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_fabric.png b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_fabric.png Binary files differnew file mode 100644 index 000000000..798e5c668 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_fabric.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_phone.png b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_phone.png Binary files differnew file mode 100644 index 000000000..1e85c4e9d --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_phone.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/loading_background.png b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/loading_background.png Binary files differnew file mode 100644 index 000000000..eb5e376e4 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/loading_background.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/take_picture.png b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/take_picture.png Binary files differnew file mode 100644 index 000000000..ef92d17dd --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/take_picture.png diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_capture.xml b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_capture.xml new file mode 100644 index 000000000..f8725175a --- /dev/null +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_capture.xml @@ -0,0 +1,46 @@ +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:bind="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + + <data> + + <variable + name="vm" + type="com.twine.colorcapture.views.capture.CaptureFragmentVM" /> + </data> + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="match_parent" tools:context="com.twine.colorcapture.views.capture.CaptureFragment"> + + <FrameLayout + android:id="@+id/surfaceViewFrame" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + </FrameLayout> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layoutDirection="ltr"> + + <ImageView + android:id="@+id/imagePreview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="centerCrop" /> + + <ImageView + android:id="@+id/imagePreviewWrapped" + android:layout_width="166dp" + android:layout_height="162dp" + android:layout_alignParentEnd="true" + android:layout_alignParentBottom="true" + android:layout_marginEnd="22dp" + android:layout_marginBottom="28dp" + app:srcCompat="@android:color/black" /> + </RelativeLayout> + </FrameLayout> +</layout> diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_home.xml b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_home.xml index b16bd7ebf..f62a2f6f2 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_home.xml +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_home.xml @@ -27,5 +27,16 @@ android:text="Home View" android:textSize="30sp" tools:text="Home View" /> + + <Button + android:id="@+id/btnStart" + android:layout_width="150dp" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" + android:layout_marginBottom="36dp" + android:text="Start" + bind:command="@{vm.startCaptureCommand}" /> + </RelativeLayout> </layout> diff --git a/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_loading.xml b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_loading.xml index be083149d..0c914ce96 100644 --- a/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_loading.xml +++ b/Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_loading.xml @@ -1,43 +1,58 @@ -<layout xmlns:tools="http://schemas.android.com/tools" - xmlns:android="http://schemas.android.com/apk/res/android" +<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:bind="http://schemas.android.com/apk/res-auto"> + xmlns:bind="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> <data> + <variable name="vm" type="com.twine.colorcapture.views.loading.LoadingFragmentVM" /> </data> - <RelativeLayout - android:layoutDirection="ltr" + <FrameLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="40dp" - android:paddingRight="40dp" - tools:context="com.twine.colorcapture.views.loading.LoadingFragment"> - + android:layout_height="match_parent"> - <TextView - android:id="@+id/textView" + <ImageView + android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:text="Loading View" - android:textSize="30sp" - tools:text="Loading View" /> + android:scaleType="fitXY" + bind:srcCompat="@drawable/loading_background" /> - <ProgressBar - android:id="@+id/progressBar" - style="?android:attr/progressBarStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@+id/textView" - android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" - android:layout_marginTop="41dp" - android:layout_marginBottom="171dp" - android:indeterminate="true" /> - </RelativeLayout> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layoutDirection="ltr" + android:paddingLeft="40dp" + android:paddingRight="40dp" + tools:context="com.twine.colorcapture.views.loading.LoadingFragment"> + + + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:text="True Color Capture" + android:textColor="@android:color/black" + android:textSize="30sp" /> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/textView" + android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" + android:layout_marginTop="41dp" + android:layout_marginBottom="171dp" + android:indeterminate="true" + android:indeterminateTint="@android:color/black" /> + </RelativeLayout> + + </FrameLayout> </layout> diff --git a/Software/Android_Studio/ColorCapture/onboarding/.gitignore b/Software/Android_Studio/ColorCapture/onboarding/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/.gitignore @@ -0,0 +1 @@ +/build diff --git a/Software/Android_Studio/ColorCapture/onboarding/build.gradle b/Software/Android_Studio/ColorCapture/onboarding/build.gradle new file mode 100644 index 000000000..2be0dc729 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 27 + + + + defaultConfig { + minSdkVersion 22 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:appcompat-v7:27.1.1' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/proguard-rules.pro b/Software/Android_Studio/ColorCapture/onboarding/proguard-rules.pro new file mode 100644 index 000000000..f1b424510 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/androidTest/java/com/twine/onboarding/ExampleInstrumentedTest.java b/Software/Android_Studio/ColorCapture/onboarding/src/androidTest/java/com/twine/onboarding/ExampleInstrumentedTest.java new file mode 100644 index 000000000..588cfb192 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/androidTest/java/com/twine/onboarding/ExampleInstrumentedTest.java @@ -0,0 +1,28 @@ +package com.twine.onboarding; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest +{ + @Test + public void useAppContext() + { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.twine.onboarding.test", appContext.getPackageName()); + } +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/AndroidManifest.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a99b1706e --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.twine.onboarding" /> diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingEngine.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingEngine.java new file mode 100644 index 000000000..aa8b066ef --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingEngine.java @@ -0,0 +1,542 @@ +package com.twine.onboarding; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.twine.onboarding.listeners.AnimatorEndListener; +import com.twine.onboarding.listeners.OnSwipeListener; +import com.twine.onboarding.listeners.PaperOnboardingOnChangeListener; +import com.twine.onboarding.listeners.PaperOnboardingOnLeftOutListener; +import com.twine.onboarding.listeners.PaperOnboardingOnRightOutListener; +import com.twine.onboarding.utils.PaperOnboardingEngineDefaults; + +import java.util.ArrayList; + +/** + * Main Paper Onboarding logic + */ +public class PaperOnboardingEngine implements PaperOnboardingEngineDefaults { + + // scale factor for converting dp to px + private final float dpToPixelsScaleFactor; + + // main layout parts + private final RelativeLayout mRootLayout; + private final FrameLayout mContentTextContainer; + private final FrameLayout mContentIconContainer; + private final FrameLayout mBackgroundContainer; + private final LinearLayout mPagerIconsContainer; + + private final RelativeLayout mContentRootLayout; + private final LinearLayout mContentCenteredContainer; + + // application context + private final Context mAppContext; + + // state variables + private ArrayList<PaperOnboardingPage> mElements = new ArrayList<>(); + private int mActiveElementIndex = 0; + + // params for Pager position calculations, virtually final, but initializes in onGlobalLayoutListener + private int mPagerElementActiveSize; + private int mPagerElementNormalSize; + private int mPagerElementLeftMargin; + private int mPagerElementRightMargin; + + // Listeners + private PaperOnboardingOnChangeListener mOnChangeListener; + private PaperOnboardingOnRightOutListener mOnRightOutListener; + private PaperOnboardingOnLeftOutListener mOnLeftOutListener; + + /** + * Main constructor for create a Paper Onboarding Engine + * + * @param rootLayout root paper onboarding layout element + * @param contentElements ordered list of prepared content elements for onboarding + * @param appContext application context + */ + public PaperOnboardingEngine(View rootLayout, ArrayList<PaperOnboardingPage> contentElements, Context appContext) { + if (contentElements == null || contentElements.isEmpty()) + throw new IllegalArgumentException("No content elements provided"); + + this.mElements.addAll(contentElements); + this.mAppContext = appContext.getApplicationContext(); + + mRootLayout = (RelativeLayout) rootLayout; + mContentTextContainer = (FrameLayout) rootLayout.findViewById(R.id.onboardingContentTextContainer); + mContentIconContainer = (FrameLayout) rootLayout.findViewById(R.id.onboardingContentIconContainer); + mBackgroundContainer = (FrameLayout) rootLayout.findViewById(R.id.onboardingBackgroundContainer); + mPagerIconsContainer = (LinearLayout) rootLayout.findViewById(R.id.onboardingPagerIconsContainer); + + mContentRootLayout = (RelativeLayout) mRootLayout.getChildAt(1); + mContentCenteredContainer = (LinearLayout) mContentRootLayout.getChildAt(0); + + this.dpToPixelsScaleFactor = this.mAppContext.getResources().getDisplayMetrics().density; + + initializeStartingState(); + + mRootLayout.setOnTouchListener(new OnSwipeListener(mAppContext) { + @Override + public void onSwipeLeft() { + toggleContent(false); + } + + @Override + public void onSwipeRight() { + toggleContent(true); + } + + }); + + mRootLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mRootLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mRootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + + mPagerElementActiveSize = mPagerIconsContainer.getHeight(); + mPagerElementNormalSize = Math.min(mPagerIconsContainer.getChildAt(0).getHeight(), + mPagerIconsContainer.getChildAt(mPagerIconsContainer.getChildCount() - 1).getHeight()); + + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mPagerIconsContainer.getChildAt(0).getLayoutParams(); + mPagerElementLeftMargin = layoutParams.leftMargin; + mPagerElementRightMargin = layoutParams.rightMargin; + + mPagerIconsContainer.setX(calculateNewPagerPosition(0)); + mContentCenteredContainer.setY((mContentRootLayout.getHeight() - mContentCenteredContainer.getHeight()) / 2); + + } + }); + } + + /** + * Calculate new position for pager without using pager's current position(like .getX()) + * this method allows to avoid incorrect position values while animation of pager in progress + * + * @param newActiveElement index of newly active element (from 0) + * @return new X position for pager bar + */ + protected int calculateNewPagerPosition(int newActiveElement) { + newActiveElement++; + if (newActiveElement <= 0) + newActiveElement = 1; + int pagerActiveElemCenterPosX = mPagerElementActiveSize / 2 + + newActiveElement * mPagerElementLeftMargin + + (newActiveElement - 1) * (mPagerElementNormalSize + mPagerElementRightMargin); + return mRootLayout.getWidth() / 2 - pagerActiveElemCenterPosX; + } + + /** + * Calculate current center coordinates of pager element with provided index + * + * @param activeElementIndex index of element (from 0) + * @return array with 2 coordinate values [x,y] + */ + protected int[] calculateCurrentCenterCoordinatesOfPagerElement(int activeElementIndex) { + int y = (int) (mPagerIconsContainer.getY() + mPagerIconsContainer.getHeight() / 2); + + if (activeElementIndex >= mPagerIconsContainer.getChildCount()) + return new int[]{mRootLayout.getWidth() / 2, y}; + + View pagerElem = mPagerIconsContainer.getChildAt(activeElementIndex); + int x = (int) (mPagerIconsContainer.getX() + pagerElem.getX() + pagerElem.getWidth() / 2); + return new int[]{x, y}; + } + + /** + * Initializes starting state + */ + protected void initializeStartingState() { + // Create bottom bar icons for all elements with big first icon + for (int i = 0; i < mElements.size(); i++) { + PaperOnboardingPage PaperOnboardingPage = mElements.get(i); + ViewGroup bottomBarIconElement = createPagerIconElement(PaperOnboardingPage.getBottomBarIconRes(), i == 0); + mPagerIconsContainer.addView(bottomBarIconElement); + } + // Initialize first element on screen + PaperOnboardingPage activeElement = getActiveElement(); + // initial content texts + ViewGroup initialContentText = createContentTextView(activeElement); + mContentTextContainer.addView(initialContentText); + // initial content icons + ImageView initContentIcon = createContentIconView(activeElement); + mContentIconContainer.addView(initContentIcon); + // initial bg color + mRootLayout.setBackgroundColor(activeElement.getBgColor()); + } + + /** + * @param prev set true to animate onto previous content page (default is false - animating to next content page) + */ + protected void toggleContent(boolean prev) { + int oldElementIndex = mActiveElementIndex; + PaperOnboardingPage newElement = prev ? toggleToPreviousElement() : toggleToNextElement(); + + if (newElement == null) { + if (prev && mOnLeftOutListener != null) + mOnLeftOutListener.onLeftOut(); + if (!prev && mOnRightOutListener != null) + mOnRightOutListener.onRightOut(); + return; + } + + int newPagerPosX = calculateNewPagerPosition(mActiveElementIndex); + + // 1 - animate BG + AnimatorSet bgAnimation = createBGAnimatorSet(newElement.getBgColor()); + + // 2 - animate pager position + Animator pagerMoveAnimation = ObjectAnimator.ofFloat(mPagerIconsContainer, "x", mPagerIconsContainer.getX(), newPagerPosX); + pagerMoveAnimation.setDuration(ANIM_PAGER_BAR_MOVE_TIME); + + // 3 - animate pager icons + AnimatorSet pagerIconAnimation = createPagerIconAnimation(oldElementIndex, mActiveElementIndex); + + // 4 animate content text + ViewGroup newContentText = createContentTextView(newElement); + mContentTextContainer.addView(newContentText); + AnimatorSet contentTextShowAnimation = createContentTextShowAnimation( + mContentTextContainer.getChildAt(mContentTextContainer.getChildCount() - 2), newContentText); + + // 5 animate content icon + ImageView newContentIcon = createContentIconView(newElement); + mContentIconContainer.addView(newContentIcon); + AnimatorSet contentIconShowAnimation = createContentIconShowAnimation( + mContentIconContainer.getChildAt(mContentIconContainer.getChildCount() - 2), newContentIcon); + + // 6 animate centering of all content + Animator centerContentAnimation = createContentCenteringVerticalAnimation(newContentText, newContentIcon); + + centerContentAnimation.start(); + bgAnimation.start(); + pagerMoveAnimation.start(); + pagerIconAnimation.start(); + contentIconShowAnimation.start(); + contentTextShowAnimation.start(); + + if (mOnChangeListener != null) + mOnChangeListener.onPageChanged(oldElementIndex, mActiveElementIndex); + } + + public void setOnChangeListener(PaperOnboardingOnChangeListener onChangeListener) { + this.mOnChangeListener = onChangeListener; + } + + public void setOnRightOutListener(PaperOnboardingOnRightOutListener onRightOutListener) { + this.mOnRightOutListener = onRightOutListener; + } + + public void setOnLeftOutListener(PaperOnboardingOnLeftOutListener onLeftOutListener) { + this.mOnLeftOutListener = onLeftOutListener; + } + + /** + * @param color new background color for new + * @return animator set with background color circular reveal animation + */ + protected AnimatorSet createBGAnimatorSet(final int color) { + final View bgColorView = new ImageView(mAppContext); + bgColorView.setLayoutParams(new RelativeLayout.LayoutParams(mRootLayout.getWidth(), mRootLayout.getHeight())); + bgColorView.setBackgroundColor(color); + mBackgroundContainer.addView(bgColorView); + + int[] pos = calculateCurrentCenterCoordinatesOfPagerElement(mActiveElementIndex); + + float finalRadius = mRootLayout.getWidth() > mRootLayout.getHeight() ? mRootLayout.getWidth() : mRootLayout.getHeight(); + + AnimatorSet bgAnimSet = new AnimatorSet(); + Animator fadeIn = ObjectAnimator.ofFloat(bgColorView, "alpha", 0, 1); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Animator circularReveal = ViewAnimationUtils.createCircularReveal(bgColorView, pos[0], pos[1], 0, finalRadius); + circularReveal.setInterpolator(new AccelerateInterpolator()); + bgAnimSet.playTogether(circularReveal, fadeIn); + } else { + bgAnimSet.playTogether(fadeIn); + } + + bgAnimSet.setDuration(ANIM_BACKGROUND_TIME); + bgAnimSet.addListener(new AnimatorEndListener() { + @Override + public void onAnimationEnd(Animator animation) { + mRootLayout.setBackgroundColor(color); + bgColorView.setVisibility(View.GONE); + mBackgroundContainer.removeView(bgColorView); + } + }); + return bgAnimSet; + } + + /** + * @param currentContentText currently displayed view with text + * @param newContentText newly created and prepared view to display + * @return animator set with this animation + */ + private AnimatorSet createContentTextShowAnimation(final View currentContentText, final View newContentText) { + int positionDeltaPx = dpToPixels(CONTENT_TEXT_POS_DELTA_Y_DP); + AnimatorSet animations = new AnimatorSet(); + Animator currentContentMoveUp = ObjectAnimator.ofFloat(currentContentText, "y", 0, -positionDeltaPx); + currentContentMoveUp.setDuration(ANIM_CONTENT_TEXT_HIDE_TIME); + currentContentMoveUp.addListener(new AnimatorEndListener() { + @Override + public void onAnimationEnd(Animator animation) { + mContentTextContainer.removeView(currentContentText); + } + }); + Animator currentContentFadeOut = ObjectAnimator.ofFloat(currentContentText, "alpha", 1, 0); + currentContentFadeOut.setDuration(ANIM_CONTENT_TEXT_HIDE_TIME); + + animations.playTogether(currentContentMoveUp, currentContentFadeOut); + + Animator newContentMoveUp = ObjectAnimator.ofFloat(newContentText, "y", positionDeltaPx, 0); + newContentMoveUp.setDuration(ANIM_CONTENT_TEXT_SHOW_TIME); + + Animator newContentFadeIn = ObjectAnimator.ofFloat(newContentText, "alpha", 0, 1); + newContentFadeIn.setDuration(ANIM_CONTENT_TEXT_SHOW_TIME); + + animations.playTogether(newContentMoveUp, newContentFadeIn); + + animations.setInterpolator(new DecelerateInterpolator()); + + return animations; + } + + /** + * @param currentContentIcon currently displayed view with icon + * @param newContentIcon newly created and prepared view to display + * @return animator set with this animation + */ + protected AnimatorSet createContentIconShowAnimation(final View currentContentIcon, final View newContentIcon) { + int positionDeltaPx = dpToPixels(CONTENT_ICON_POS_DELTA_Y_DP); + AnimatorSet animations = new AnimatorSet(); + Animator currentContentMoveUp = ObjectAnimator.ofFloat(currentContentIcon, "y", 0, -positionDeltaPx); + currentContentMoveUp.setDuration(ANIM_CONTENT_ICON_HIDE_TIME); + + currentContentMoveUp.addListener(new AnimatorEndListener() { + @Override + public void onAnimationEnd(Animator animation) { + mContentIconContainer.removeView(currentContentIcon); + } + }); + Animator currentContentFadeOut = ObjectAnimator.ofFloat(currentContentIcon, "alpha", 1, 0); + currentContentFadeOut.setDuration(ANIM_CONTENT_ICON_HIDE_TIME); + + animations.playTogether(currentContentMoveUp, currentContentFadeOut); + + Animator newContentMoveUp = ObjectAnimator.ofFloat(newContentIcon, "y", positionDeltaPx, 0); + newContentMoveUp.setDuration(ANIM_CONTENT_ICON_SHOW_TIME); + + Animator newContentFadeIn = ObjectAnimator.ofFloat(newContentIcon, "alpha", 0, 1); + newContentFadeIn.setDuration(ANIM_CONTENT_ICON_SHOW_TIME); + + animations.playTogether(newContentMoveUp, newContentFadeIn); + + animations.setInterpolator(new DecelerateInterpolator()); + + return animations; + } + + protected Animator createContentCenteringVerticalAnimation(View newContentText, View newContentIcon) { + newContentText.measure(View.MeasureSpec.makeMeasureSpec(mContentCenteredContainer.getWidth(), View.MeasureSpec.AT_MOST), -2); + int measuredContentTextHeight = newContentText.getMeasuredHeight(); + newContentIcon.measure(-2, -2); + int measuredContentIconHeight = newContentIcon.getMeasuredHeight(); + + int newHeightOfContent = measuredContentIconHeight + measuredContentTextHeight + ((ViewGroup.MarginLayoutParams) mContentTextContainer.getLayoutParams()).topMargin; + Animator centerContentAnimation = ObjectAnimator.ofFloat(mContentCenteredContainer, "y", mContentCenteredContainer.getY(), + (mContentRootLayout.getHeight() - newHeightOfContent) / 2); + centerContentAnimation.setDuration(ANIM_CONTENT_CENTERING_TIME); + centerContentAnimation.setInterpolator(new DecelerateInterpolator()); + return centerContentAnimation; + } + + /** + * Create animator for pager icon + * + * @param oldIndex index currently active icon + * @param newIndex index of new active icon + * @return animator set with this animation + */ + protected AnimatorSet createPagerIconAnimation(int oldIndex, int newIndex) { + AnimatorSet animations = new AnimatorSet(); + animations.setDuration(ANIM_PAGER_ICON_TIME); + + // scale down whole old element + final ViewGroup oldActiveItem = (ViewGroup) mPagerIconsContainer.getChildAt(oldIndex); + final LinearLayout.LayoutParams oldActiveItemParams = (LinearLayout.LayoutParams) oldActiveItem.getLayoutParams(); + ValueAnimator oldItemScaleDown = ValueAnimator.ofInt(mPagerElementActiveSize, mPagerElementNormalSize); + oldItemScaleDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + oldActiveItemParams.height = (Integer) valueAnimator.getAnimatedValue(); + oldActiveItemParams.width = (Integer) valueAnimator.getAnimatedValue(); + oldActiveItem.requestLayout(); + } + }); + + // fade out old new element icon + final View oldActiveIcon = oldActiveItem.getChildAt(1); + Animator oldActiveIconFadeOut = ObjectAnimator.ofFloat(oldActiveIcon, "alpha", 1, 0); + + // fade in old element shape + final ImageView oldActiveShape = (ImageView) oldActiveItem.getChildAt(0); + oldActiveShape.setImageResource(oldIndex - newIndex > 0 ? R.drawable.onboarding_pager_circle_icon : R.drawable.onboarding_pager_round_icon); + Animator oldActiveShapeFadeIn = ObjectAnimator.ofFloat(oldActiveShape, "alpha", 0, PAGER_ICON_SHAPE_ALPHA); + // add animations + animations.playTogether(oldItemScaleDown, oldActiveIconFadeOut, oldActiveShapeFadeIn); + + // scale up whole new element + final ViewGroup newActiveItem = (ViewGroup) mPagerIconsContainer.getChildAt(newIndex); + final LinearLayout.LayoutParams newActiveItemParams = (LinearLayout.LayoutParams) newActiveItem.getLayoutParams(); + ValueAnimator newItemScaleUp = ValueAnimator.ofInt(mPagerElementNormalSize, mPagerElementActiveSize); + newItemScaleUp.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + newActiveItemParams.height = (Integer) valueAnimator.getAnimatedValue(); + newActiveItemParams.width = (Integer) valueAnimator.getAnimatedValue(); + newActiveItem.requestLayout(); + } + }); + + // fade in new element icon + final View newActiveIcon = newActiveItem.getChildAt(1); + Animator newActiveIconFadeIn = ObjectAnimator.ofFloat(newActiveIcon, "alpha", 0, 1); + + // fade out new element shape + final ImageView newActiveShape = (ImageView) newActiveItem.getChildAt(0); + Animator newActiveShapeFadeOut = ObjectAnimator.ofFloat(newActiveShape, "alpha", PAGER_ICON_SHAPE_ALPHA, 0); + + // add animations + animations.playTogether(newItemScaleUp, newActiveShapeFadeOut, newActiveIconFadeIn); + + animations.setInterpolator(new DecelerateInterpolator()); + return animations; + } + + /** + * @param iconDrawableRes drawable resource for icon + * @param isActive is active element + * @return configured pager icon with selected drawable and selected state (active or inactive) + */ + @SuppressWarnings("SuspiciousNameCombination") + protected ViewGroup createPagerIconElement(int iconDrawableRes, boolean isActive) { + LayoutInflater vi = LayoutInflater.from(mAppContext); + FrameLayout bottomBarElement = (FrameLayout) vi.inflate(R.layout.onboarding_pager_layout, mPagerIconsContainer, false); + ImageView elementShape = (ImageView) bottomBarElement.getChildAt(0); + ImageView elementIcon = (ImageView) bottomBarElement.getChildAt(1); + elementIcon.setImageResource(iconDrawableRes); + if (isActive) { + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) bottomBarElement.getLayoutParams(); + layoutParams.width = mPagerIconsContainer.getLayoutParams().height; + layoutParams.height = mPagerIconsContainer.getLayoutParams().height; + elementShape.setAlpha(0f); + elementIcon.setAlpha(1f); + } else { + elementShape.setAlpha(PAGER_ICON_SHAPE_ALPHA); + elementIcon.setAlpha(0f); + } + return bottomBarElement; + } + + /** + * @param PaperOnboardingPage new content page to show + * @return configured view with new content texts + */ + protected ViewGroup createContentTextView(PaperOnboardingPage PaperOnboardingPage) { + LayoutInflater vi = LayoutInflater.from(mAppContext); + ViewGroup contentTextView = (ViewGroup) vi.inflate(R.layout.onboarding_text_content_layout, mContentTextContainer, false); + TextView contentTitle = (TextView) contentTextView.getChildAt(0); + contentTitle.setText(PaperOnboardingPage.getTitleText()); + TextView contentText = (TextView) contentTextView.getChildAt(1); + contentText.setText(PaperOnboardingPage.getDescriptionText()); + return contentTextView; + } + + /** + * @param PaperOnboardingPage new content page to show + * @return configured view with new content image + */ + protected ImageView createContentIconView(PaperOnboardingPage PaperOnboardingPage) { + ImageView contentIcon = new ImageView(mAppContext); + contentIcon.setImageResource(PaperOnboardingPage.getContentIconRes()); + FrameLayout.LayoutParams iconLP = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + iconLP.gravity = Gravity.CENTER; + contentIcon.setLayoutParams(iconLP); + return contentIcon; + } + + /** + * @return index of currently active element + */ + public int getActiveElementIndex() { + return mActiveElementIndex; + } + + /** + * Returns content for currently active element + * + * @return content for currently active element + */ + protected PaperOnboardingPage getActiveElement() { + return mElements.size() > mActiveElementIndex ? mElements.get(mActiveElementIndex) : null; + } + + /** + * Changes active element to the previous one and returns a new content + * + * @return content for previous element + */ + protected PaperOnboardingPage toggleToPreviousElement() { + if (mActiveElementIndex - 1 >= 0) { + mActiveElementIndex--; + return mElements.size() > mActiveElementIndex ? mElements.get(mActiveElementIndex) : null; + } else + return null; + } + + /** + * Changes active element to the next one and returns a new content + * + * @return content for next element + */ + protected PaperOnboardingPage toggleToNextElement() { + if (mActiveElementIndex + 1 < mElements.size()) { + mActiveElementIndex++; + return mElements.size() > mActiveElementIndex ? mElements.get(mActiveElementIndex) : null; + } else + return null; + } + + /** + * Converts DP values to PX + * + * @param dpValue value to convert in dp + * @return converted value in px + */ + protected int dpToPixels(int dpValue) { + return (int) (dpValue * dpToPixelsScaleFactor + 0.5f); + } + + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingFragment.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingFragment.java new file mode 100644 index 000000000..90d408600 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingFragment.java @@ -0,0 +1,81 @@ +package com.twine.onboarding; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.twine.onboarding.listeners.PaperOnboardingOnChangeListener; +import com.twine.onboarding.listeners.PaperOnboardingOnLeftOutListener; +import com.twine.onboarding.listeners.PaperOnboardingOnRightOutListener; + +import java.util.ArrayList; + +/** + * Ready to use PaperOnboarding fragment + */ +public class PaperOnboardingFragment extends Fragment +{ + + private static final String ELEMENTS_PARAM = "elements"; + + private PaperOnboardingOnChangeListener mOnChangeListener; + private PaperOnboardingOnRightOutListener mOnRightOutListener; + private PaperOnboardingOnLeftOutListener mOnLeftOutListener; + private ArrayList<PaperOnboardingPage> mElements; + + + public static PaperOnboardingFragment newInstance(ArrayList<PaperOnboardingPage> elements) { + PaperOnboardingFragment fragment = new PaperOnboardingFragment(); + Bundle args = new Bundle(); + args.putSerializable(ELEMENTS_PARAM, elements); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mElements = (ArrayList<PaperOnboardingPage>) getArguments().get(ELEMENTS_PARAM); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.onboarding_main_layout, container, false); + + // create engine for onboarding element + PaperOnboardingEngine mPaperOnboardingEngine = new PaperOnboardingEngine(view.findViewById(R.id.onboardingRootView), mElements, getActivity().getApplicationContext()); + // set listeners + mPaperOnboardingEngine.setOnChangeListener(mOnChangeListener); + mPaperOnboardingEngine.setOnLeftOutListener(mOnLeftOutListener); + mPaperOnboardingEngine.setOnRightOutListener(mOnRightOutListener); + + return view; + } + + public void setElements(ArrayList<PaperOnboardingPage> elements) { + this.mElements = elements; + } + + public ArrayList<PaperOnboardingPage> getElements() { + return mElements; + } + + public void setOnChangeListener(PaperOnboardingOnChangeListener onChangeListener) { + this.mOnChangeListener = onChangeListener; + } + + public void setOnRightOutListener(PaperOnboardingOnRightOutListener onRightOutListener) { + this.mOnRightOutListener = onRightOutListener; + } + + public void setOnLeftOutListener(PaperOnboardingOnLeftOutListener onLeftOutListener) { + this.mOnLeftOutListener = onLeftOutListener; + } + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingPage.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingPage.java new file mode 100644 index 000000000..8104e4329 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingPage.java @@ -0,0 +1,103 @@ +package com.twine.onboarding; + +import java.io.Serializable; + +/** + * Represents content for one page of Paper Onboarding + */ +public class PaperOnboardingPage implements Serializable { + + private String titleText; + private String descriptionText; + private int bgColor; + private int contentIconRes; + private int bottomBarIconRes; + + public PaperOnboardingPage() { + } + + public PaperOnboardingPage(String titleText, String descriptionText, int bgColor, int contentIconRes, int bottomBarIconRes) { + this.bgColor = bgColor; + this.contentIconRes = contentIconRes; + this.bottomBarIconRes = bottomBarIconRes; + this.descriptionText = descriptionText; + this.titleText = titleText; + } + + public String getTitleText() { + return titleText; + } + + public void setTitleText(String titleText) { + this.titleText = titleText; + } + + public String getDescriptionText() { + return descriptionText; + } + + public void setDescriptionText(String descriptionText) { + this.descriptionText = descriptionText; + } + + public int getContentIconRes() { + return contentIconRes; + } + + public void setContentIconRes(int contentIconRes) { + this.contentIconRes = contentIconRes; + } + + public int getBottomBarIconRes() { + return bottomBarIconRes; + } + + public void setBottomBarIconRes(int bottomBarIconRes) { + this.bottomBarIconRes = bottomBarIconRes; + } + + public int getBgColor() { + return bgColor; + } + + public void setBgColor(int bgColor) { + this.bgColor = bgColor; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PaperOnboardingPage that = (PaperOnboardingPage) o; + + if (bgColor != that.bgColor) return false; + if (contentIconRes != that.contentIconRes) return false; + if (bottomBarIconRes != that.bottomBarIconRes) return false; + if (titleText != null ? !titleText.equals(that.titleText) : that.titleText != null) + return false; + return descriptionText != null ? descriptionText.equals(that.descriptionText) : that.descriptionText == null; + + } + + @Override + public int hashCode() { + int result = titleText != null ? titleText.hashCode() : 0; + result = 31 * result + (descriptionText != null ? descriptionText.hashCode() : 0); + result = 31 * result + bgColor; + result = 31 * result + contentIconRes; + result = 31 * result + bottomBarIconRes; + return result; + } + + @Override + public String toString() { + return "PaperOnboardingPage{" + + "titleText='" + titleText + '\'' + + ", descriptionText='" + descriptionText + '\'' + + ", bgColor=" + bgColor + + ", contentIconRes=" + contentIconRes + + ", bottomBarIconRes=" + bottomBarIconRes + + '}'; + } +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/AnimatorEndListener.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/AnimatorEndListener.java new file mode 100644 index 000000000..9383f1227 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/AnimatorEndListener.java @@ -0,0 +1,24 @@ +package com.twine.onboarding.listeners; + +import android.animation.Animator; + +/** + * Just sugar for code clean + */ +public abstract class AnimatorEndListener implements Animator.AnimatorListener { + + @Override + public void onAnimationStart(Animator animation) { + //do nothing + } + + @Override + public void onAnimationCancel(Animator animation) { + //do nothing + } + + @Override + public void onAnimationRepeat(Animator animation) { + //do nothing + } +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/OnSwipeListener.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/OnSwipeListener.java new file mode 100644 index 000000000..0d0c78a1c --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/OnSwipeListener.java @@ -0,0 +1,77 @@ +package com.twine.onboarding.listeners; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; + +public abstract class OnSwipeListener implements View.OnTouchListener { + + private final GestureDetector gestureDetector; + + public OnSwipeListener(Context ctx) { + gestureDetector = new GestureDetector(ctx, new GestureListener()); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + + public void onSwipeRight() { + // do nothing here + } + + public void onSwipeLeft() { + // do nothing here + } + + public void onSwipeTop() { + // do nothing here + } + + public void onSwipeBottom() { + // do nothing here + } + + private final class GestureListener extends GestureDetector.SimpleOnGestureListener { + + private static final int SWIPE_THRESHOLD = 100; + private static final int SWIPE_VELOCITY_THRESHOLD = 100; + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + boolean result = false; + try { + float diffY = e2.getY() - e1.getY(); + float diffX = e2.getX() - e1.getX(); + if (Math.abs(diffX) > Math.abs(diffY)) { + if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { + if (diffX > 0) { + onSwipeRight(); + } else { + onSwipeLeft(); + } + } + result = true; + } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { + if (diffY > 0) { + onSwipeBottom(); + } else { + onSwipeTop(); + } + } + result = true; + + } catch (Exception exception) { + exception.printStackTrace(); + } + return result; + } + } +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnChangeListener.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnChangeListener.java new file mode 100644 index 000000000..975ac0f54 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnChangeListener.java @@ -0,0 +1,7 @@ +package com.twine.onboarding.listeners; + +public interface PaperOnboardingOnChangeListener { + + void onPageChanged(int oldElementIndex, int newElementIndex); + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnLeftOutListener.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnLeftOutListener.java new file mode 100644 index 000000000..3f47e2fdf --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnLeftOutListener.java @@ -0,0 +1,7 @@ +package com.twine.onboarding.listeners; + +public interface PaperOnboardingOnLeftOutListener { + + void onLeftOut(); + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnRightOutListener.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnRightOutListener.java new file mode 100644 index 000000000..09c7bf241 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnRightOutListener.java @@ -0,0 +1,7 @@ +package com.twine.onboarding.listeners; + +public interface PaperOnboardingOnRightOutListener { + + void onRightOut(); + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/utils/PaperOnboardingEngineDefaults.java b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/utils/PaperOnboardingEngineDefaults.java new file mode 100644 index 000000000..2ef3aa23d --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/utils/PaperOnboardingEngineDefaults.java @@ -0,0 +1,28 @@ +package com.twine.onboarding.utils; + +/** + * Default values for everything + */ +public interface PaperOnboardingEngineDefaults { + String TAG = "POB"; + + // animation and view settings + int ANIM_PAGER_BAR_MOVE_TIME = 700; + + int ANIM_PAGER_ICON_TIME = 350; + + int ANIM_BACKGROUND_TIME = 450; + + int CONTENT_TEXT_POS_DELTA_Y_DP = 50; + int ANIM_CONTENT_TEXT_SHOW_TIME = 800; + int ANIM_CONTENT_TEXT_HIDE_TIME = 200; + + int CONTENT_ICON_POS_DELTA_Y_DP = 50; + int ANIM_CONTENT_ICON_SHOW_TIME = 800; + int ANIM_CONTENT_ICON_HIDE_TIME = 200; + + float PAGER_ICON_SHAPE_ALPHA = 0.5f; + + int ANIM_CONTENT_CENTERING_TIME = 800; + +} diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_circle_icon.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_circle_icon.xml new file mode 100644 index 000000000..1010a7f75 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_circle_icon.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <stroke + android:width="3dp" + android:color="#e6e6e6" /> + +</shape>
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_round_icon.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_round_icon.xml new file mode 100644 index 000000000..2cf2ee92b --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_round_icon.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid android:color="#e6e6e6" /> + +</shape>
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_main_layout.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_main_layout.xml new file mode 100644 index 000000000..03682445f --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_main_layout.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/onboardingRootView" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- BG COLORS CONTAINER --> + <FrameLayout + android:id="@+id/onboardingBackgroundContainer" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <!-- MAIN LAYOUT SECTION --> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginBottom="65dp" + android:animateLayoutChanges="true" + android:clipChildren="false"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginLeft="30dp" + android:layout_marginRight="30dp" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="vertical"> + + <!-- ICON CONTAINER --> + <FrameLayout + android:id="@+id/onboardingContentIconContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:clipChildren="false" + android:clipToPadding="false" /> + + <!-- TEXT CONTAINER --> + <FrameLayout + android:id="@+id/onboardingContentTextContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="30dp" + android:clipChildren="false" + android:clipToPadding="false" /> + + </LinearLayout> + + </RelativeLayout> + + <!-- PAGER ICONS CONTAINER --> + <LinearLayout + android:id="@+id/onboardingPagerIconsContainer" + android:layout_width="wrap_content" + android:layout_height="40dp" + android:layout_alignParentBottom="true" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginBottom="25dp" + android:baselineAligned="false" + android:gravity="center_vertical" + android:orientation="horizontal" /> + + +</RelativeLayout> diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_pager_layout.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_pager_layout.xml new file mode 100644 index 000000000..cd4600c97 --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_pager_layout.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="15dp" + android:layout_height="15dp" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="onboarding pager icon shape" + android:src="@drawable/onboarding_pager_circle_icon" /> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="onboarding pager icon image" /> + +</FrameLayout>
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_text_content_layout.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_text_content_layout.xml new file mode 100644 index 000000000..dc24fce3b --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_text_content_layout.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="10dp" + android:text="Content title" + android:textAlignment="center" + android:textSize="32sp" + android:textStyle="bold" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:text="Content text" + android:textAlignment="center" + android:textSize="16sp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/main/res/values/strings.xml b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/values/strings.xml new file mode 100644 index 000000000..f7617b27d --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">paper-onboarding</string> +</resources> diff --git a/Software/Android_Studio/ColorCapture/onboarding/src/test/java/com/twine/onboarding/ExampleUnitTest.java b/Software/Android_Studio/ColorCapture/onboarding/src/test/java/com/twine/onboarding/ExampleUnitTest.java new file mode 100644 index 000000000..d143e4bee --- /dev/null +++ b/Software/Android_Studio/ColorCapture/onboarding/src/test/java/com/twine/onboarding/ExampleUnitTest.java @@ -0,0 +1,19 @@ +package com.twine.onboarding; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +public class ExampleUnitTest +{ + @Test + public void addition_isCorrect() + { + assertEquals(4, 2 + 2); + } +}
\ No newline at end of file diff --git a/Software/Android_Studio/ColorCapture/settings.gradle b/Software/Android_Studio/ColorCapture/settings.gradle index e7b4def49..48771db44 100644 --- a/Software/Android_Studio/ColorCapture/settings.gradle +++ b/Software/Android_Studio/ColorCapture/settings.gradle @@ -1 +1 @@ -include ':app' +include ':app', ':onboarding' |
