aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Android_Studio
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Android_Studio')
-rw-r--r--Software/Android_Studio/ColorCapture/app/build.gradle5
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/color_fine_tuning-web.pngbin0 -> 21864 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/cpp/native-lib.cpp132
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ApplicationComponent.java3
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/dagger/ViewModelsModule.java10
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/FragmentBase.java14
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/mvvm/RelayCommand.java2
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/navigation/NavigationView.java1
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/opencv/ImageProcessor.java6
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragment.java249
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/CaptureFragmentVM.java8
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/capture/ICaptureFragment.java7
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragment.java43
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/HomeFragmentVM.java18
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/home/IHomeFragment.java4
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivity.java34
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/java/com/twine/colorcapture/views/main/MainActivityVM.java7
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/drawable/application_logs.pngbin0 -> 16421 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_fabric.pngbin0 -> 231422 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_phone.pngbin0 -> 268124 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/drawable/loading_background.pngbin0 -> 347710 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/drawable/take_picture.pngbin0 -> 232959 bytes
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_capture.xml46
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_home.xml11
-rw-r--r--Software/Android_Studio/ColorCapture/app/src/main/res/layout/fragment_loading.xml73
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/.gitignore1
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/build.gradle34
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/proguard-rules.pro21
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/androidTest/java/com/twine/onboarding/ExampleInstrumentedTest.java28
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/AndroidManifest.xml2
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingEngine.java542
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingFragment.java81
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/PaperOnboardingPage.java103
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/AnimatorEndListener.java24
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/OnSwipeListener.java77
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnChangeListener.java7
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnLeftOutListener.java7
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/listeners/PaperOnboardingOnRightOutListener.java7
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/java/com/twine/onboarding/utils/PaperOnboardingEngineDefaults.java28
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_circle_icon.xml9
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/drawable/onboarding_pager_round_icon.xml7
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_main_layout.xml67
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_pager_layout.xml19
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/layout/onboarding_text_content_layout.xml25
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/main/res/values/strings.xml3
-rw-r--r--Software/Android_Studio/ColorCapture/onboarding/src/test/java/com/twine/onboarding/ExampleUnitTest.java19
-rw-r--r--Software/Android_Studio/ColorCapture/settings.gradle2
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
new file mode 100644
index 000000000..7f1c46806
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/color_fine_tuning-web.png
Binary files differ
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
new file mode 100644
index 000000000..584379dc8
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/application_logs.png
Binary files differ
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
new file mode 100644
index 000000000..798e5c668
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_fabric.png
Binary files differ
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
new file mode 100644
index 000000000..1e85c4e9d
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/hand_phone.png
Binary files differ
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
new file mode 100644
index 000000000..eb5e376e4
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/loading_background.png
Binary files differ
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
new file mode 100644
index 000000000..ef92d17dd
--- /dev/null
+++ b/Software/Android_Studio/ColorCapture/app/src/main/res/drawable/take_picture.png
Binary files differ
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'