├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── app.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── codingwithmitch │ │ └── com │ │ └── tabiancustomcamera │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── codingwithmitch │ │ │ └── com │ │ │ └── tabiancustomcamera │ │ │ ├── Camera2Fragment.java │ │ │ ├── DrawableImageView.java │ │ │ ├── ICallback.java │ │ │ ├── IMainActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ScalingTextureView.java │ │ │ ├── StickerAdapter.java │ │ │ ├── Utility.java │ │ │ ├── VerticalSlideColorPicker.java │ │ │ ├── ViewStickersFragment.java │ │ │ └── gestures │ │ │ ├── BaseGestureDetector.java │ │ │ └── MoveGestureDetector.java │ └── res │ │ ├── anim │ │ ├── slide_in_down.xml │ │ ├── slide_in_up.xml │ │ ├── slide_out_down.xml │ │ └── slide_out_up.xml │ │ ├── drawable-hdpi │ │ ├── ic_flash_auto.png │ │ ├── ic_flash_off.png │ │ ├── ic_flash_on.png │ │ ├── ic_pen_small.png │ │ ├── ic_save_small.png │ │ ├── ic_sticker_small.png │ │ ├── ic_switch_camera_orient.png │ │ └── ic_undo_small.png │ │ ├── drawable-mdpi │ │ ├── ic_flash_auto.png │ │ ├── ic_flash_off.png │ │ ├── ic_flash_on.png │ │ ├── ic_pen_small.png │ │ ├── ic_save_small.png │ │ ├── ic_sticker_small.png │ │ ├── ic_switch_camera_orient.png │ │ └── ic_undo_small.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── ic_flash_auto.png │ │ ├── ic_flash_off.png │ │ ├── ic_flash_on.png │ │ ├── ic_pen_small.png │ │ ├── ic_save_small.png │ │ ├── ic_sticker_small.png │ │ ├── ic_switch_camera_orient.png │ │ └── ic_undo_small.png │ │ ├── drawable-xxhdpi │ │ ├── ic_flash_auto.png │ │ ├── ic_flash_off.png │ │ ├── ic_flash_on.png │ │ ├── ic_pen_small.png │ │ ├── ic_save_small.png │ │ ├── ic_sticker_small.png │ │ ├── ic_switch_camera_orient.png │ │ └── ic_undo_small.png │ │ ├── drawable │ │ ├── astonished_face_emoji.png │ │ ├── cam_action_capture.xml │ │ ├── cam_action_stillshot.xml │ │ ├── cam_circle_selector.xml │ │ ├── cam_grey_circle_selector.xml │ │ ├── circle_opaque.xml │ │ ├── circle_white.xml │ │ ├── cry_emoji.png │ │ ├── evil_monkey_emoji.png │ │ ├── ic_delete_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── nerd_emoji.png │ │ ├── penguin_emoji.png │ │ ├── sad_emoji.png │ │ ├── slightly_smiling_face_emoji.png │ │ ├── smiley_face_emoji.png │ │ ├── smiley_face_tightly_closed_eyes_emoji.png │ │ ├── smiley_smiling_eyes_emoji.png │ │ ├── smiley_with_sweat_emoji.png │ │ ├── smirking_emoji.png │ │ ├── sunglasses_emoji.png │ │ ├── tears_of_joy_emoji.png │ │ ├── unamused_emoji.png │ │ ├── upside_down_face_emoji.png │ │ └── x_white_border.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_camera2.xml │ │ ├── fragment_view_stickers.xml │ │ └── layout_sticker_list_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── array.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── default_colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── codingwithmitch │ └── com │ └── tabiancustomcamera │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This code is deprecated. See [CameraX](https://developer.android.com/training/camerax) 2 |
3 | 4 | 5 |

Building a Custom Camera for Android

6 |

Get the course here.

7 |

Watch the Course Demo.

8 | 9 |

What you'll learn:

10 |
    11 |
  1. Android's camera2 API
  2. 12 |
  3. Making full-screen camera previews
  4. 13 |
  5. Accommodating different screen sizes
  6. 14 |
  7. Capturing photos
  8. 15 |
  9. Drawing on photos
  10. 16 |
  11. Adding stickers
  12. 17 |
  13. Saving photos to internal storage
  14. 18 |
19 | 20 |

Submitting an Issue

21 |

Watch this video to learn about how to properly submit an issue.

22 | 23 |

Here is an example issue I made so you can get a good idea of what it should look like: Example

24 | 25 |

Link to the Phone Test: Phone Test

26 | 27 |

Phones with Known Issues

28 |
    29 |
  1. 30 |

    Redmi 4X (santoni)

    31 |

    See here for a workaround

    32 |
  2. 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "codingwithmitch.com.tabiancustomcamera" 7 | minSdkVersion 21 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:28.0.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | 29 | //Support Design Library 30 | implementation 'com.android.support:design:28.0.0' 31 | 32 | //glide 33 | implementation 'com.github.bumptech.glide:glide:4.7.1' 34 | annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' 35 | } 36 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/codingwithmitch/com/tabiancustomcamera/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("codingwithmitch.com.tabiancustomcamera", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/DrawableImageView.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Paint; 8 | import android.graphics.Path; 9 | import android.graphics.Rect; 10 | import android.graphics.drawable.BitmapDrawable; 11 | import android.graphics.drawable.Drawable; 12 | import android.util.AttributeSet; 13 | import android.util.DisplayMetrics; 14 | import android.util.Log; 15 | import android.view.MotionEvent; 16 | import android.view.ScaleGestureDetector; 17 | import android.view.View; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Created by User on 5/23/2018. 24 | */ 25 | 26 | public class DrawableImageView extends android.support.v7.widget.AppCompatImageView 27 | { 28 | 29 | private static final String TAG = "DrawableImageView"; 30 | 31 | private static final int SIZE_CHANGE_SPEED = 2; 32 | private static final int STICKER_STARTING_WIDTH = 300; 33 | private static final int STICKER_STARTING_HEIGHT = 300; 34 | private static final int MIN_STICKER_WIDTH = 50; 35 | private static final int MIN_STICKER_HEIGHT = 50; 36 | private static final int TRASH_ICON_ENLARGED_SIZE = 55; 37 | private static final int TRASH_ICON_NORMAL_SIZE = 44; 38 | 39 | //vars 40 | private int color; 41 | private float width = 8f; 42 | private List mPenList = new ArrayList(); 43 | private Activity mHostActivity; 44 | private boolean mIsDrawingEnabled = false; 45 | 46 | 47 | // Scales 48 | float mMinWidth = 8f; 49 | float mMaxWidth = 500f; 50 | private ScaleGestureDetector mScaleGestureDetector; 51 | private boolean mIsSizeChanging = false; 52 | private Circle mCircle; 53 | private int mScreenWidth; 54 | 55 | 56 | // Stickers 57 | private ArrayList mStickers = new ArrayList<>(); 58 | int mPrevStickerX, mPrevStickerY; 59 | int mSelectedStickerIndex = -1; 60 | private boolean mIsStickerResizing = false; 61 | 62 | // Trash can location 63 | Rect trashRect; 64 | 65 | private class Sticker{ 66 | 67 | Paint paint; 68 | Bitmap bitmap; 69 | Drawable drawable; 70 | int x, y; 71 | Rect rect; 72 | 73 | 74 | Sticker(Bitmap bitmap, Drawable drawable, int x, int y){ 75 | paint = new Paint(); 76 | this.x = x; 77 | this.y = y; 78 | this.bitmap = bitmap; 79 | this.drawable = drawable; 80 | rect = new Rect(x, y, x + STICKER_STARTING_WIDTH, y + STICKER_STARTING_HEIGHT); 81 | } 82 | 83 | public void adjustRect(){ 84 | rect.left = x; 85 | rect.top = y; 86 | rect.right = x + bitmap.getWidth(); 87 | rect.bottom = y + bitmap.getHeight(); 88 | } 89 | } 90 | 91 | 92 | private class Circle { 93 | 94 | float x, y; 95 | Paint paint; 96 | 97 | Circle(int color, float x, float y) { 98 | this.x = x; 99 | this.y = y; 100 | paint = new Paint(); 101 | paint.setAntiAlias(true); 102 | paint.setStrokeWidth(width); 103 | paint.setColor(color); 104 | paint.setStyle(Paint.Style.STROKE); 105 | paint.setStrokeJoin(Paint.Join.ROUND); 106 | paint.setStrokeCap(Paint.Cap.ROUND); 107 | } 108 | } 109 | 110 | private class Pen { 111 | Path path; 112 | Paint paint; 113 | 114 | Pen(int color, float width ) { 115 | path = new Path(); 116 | paint = new Paint(); 117 | paint.setAntiAlias(true); 118 | paint.setStrokeWidth(width); 119 | paint.setColor(color); 120 | paint.setStyle(Paint.Style.STROKE); 121 | paint.setStrokeJoin(Paint.Join.ROUND); 122 | paint.setStrokeCap(Paint.Cap.ROUND); 123 | } 124 | } 125 | 126 | 127 | 128 | public DrawableImageView(Context context) { 129 | super(context); 130 | init(context); 131 | } 132 | 133 | public DrawableImageView(Context context, AttributeSet attrs) { 134 | super(context, attrs); 135 | 136 | init(context); 137 | } 138 | 139 | public DrawableImageView(Context context, AttributeSet attrs, int defStyle) { 140 | super(context, attrs, defStyle); 141 | 142 | init(context); 143 | } 144 | 145 | private void init(Context context) { 146 | mPenList.add(new Pen(color, width)); 147 | setDrawingCacheEnabled(true); 148 | if(context instanceof Activity) { 149 | mHostActivity = (Activity) context; 150 | 151 | mScaleGestureDetector = new ScaleGestureDetector(mHostActivity, new ScaleListener()); 152 | 153 | DisplayMetrics displayMetrics = new DisplayMetrics(); 154 | mHostActivity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); 155 | mScreenWidth = displayMetrics.widthPixels; 156 | 157 | int screenHeight = displayMetrics.heightPixels; 158 | 159 | float density = displayMetrics.density; 160 | int bottomMargin = (int)mHostActivity.getResources().getDimension(R.dimen.cam_widget_margin_bottom); 161 | 162 | int left = (mScreenWidth / 2) - (int) ( (TRASH_ICON_NORMAL_SIZE ) * density + 0.5f); 163 | int top = screenHeight - (int) ( (bottomMargin + TRASH_ICON_NORMAL_SIZE ) * density + 0.5f);; 164 | int right = (mScreenWidth / 2) + (int) ( (TRASH_ICON_NORMAL_SIZE ) * density + 0.5f); 165 | int bottom = screenHeight; 166 | 167 | trashRect = new Rect(left, top, right, bottom); 168 | } 169 | } 170 | 171 | 172 | @Override 173 | protected void onDraw(Canvas canvas) { 174 | super.onDraw(canvas); 175 | 176 | if(mStickers.size() > 0){ 177 | for(Sticker sticker: mStickers){ 178 | canvas.drawBitmap(sticker.bitmap, sticker.x, sticker.y, sticker.paint); 179 | } 180 | } 181 | 182 | for (Pen pen : mPenList) { 183 | canvas.drawPath(pen.path, pen.paint); 184 | } 185 | 186 | if(mCircle != null){ 187 | float radius = width / (float)mScreenWidth; 188 | canvas.drawCircle(mCircle.x, mCircle.y, radius, mCircle.paint); 189 | } 190 | } 191 | 192 | 193 | public boolean touchEvent(MotionEvent event){ 194 | 195 | hideStatusBar(); 196 | 197 | switch (event.getAction()) { 198 | case MotionEvent.ACTION_UP:{ 199 | removeCircle(); 200 | mIsSizeChanging = false; 201 | break; 202 | } 203 | } 204 | 205 | if(mIsDrawingEnabled) { 206 | if (event.getPointerCount() > 1) { 207 | 208 | if (!mIsSizeChanging) { 209 | mIsSizeChanging = true; 210 | } 211 | 212 | try { 213 | mScaleGestureDetector.onTouchEvent(event); 214 | 215 | float[] xPositions = new float[2]; 216 | float[] yPositions = new float[2]; 217 | float minX = 0; 218 | float minY = 0; 219 | 220 | for (int i = 0; i < event.getPointerCount(); i++) { 221 | int pointerId = event.getPointerId(i); 222 | if (pointerId == MotionEvent.INVALID_POINTER_ID) { 223 | continue; 224 | } 225 | 226 | if (minX == 0 && minY == 0) { 227 | minX = event.getX(i); 228 | minY = event.getY(i); 229 | } else { 230 | float tempMinX = event.getX(i); 231 | float tempMinY = event.getY(i); 232 | if (tempMinX < minX) { 233 | minX = tempMinX; 234 | } 235 | if (tempMinY < minY) { 236 | minY = tempMinY; 237 | } 238 | } 239 | 240 | xPositions[i] = event.getX(i); 241 | yPositions[i] = event.getY(i); 242 | } 243 | 244 | float circleX = (Math.abs(xPositions[1] - xPositions[0]) / 2) + minX; 245 | float circleY = (Math.abs(yPositions[1] - yPositions[0]) / 2) + minY; 246 | 247 | 248 | mCircle = new Circle(color, circleX, circleY); 249 | 250 | } catch (IndexOutOfBoundsException e) { 251 | Log.e(TAG, "touchEvent: IndexOutOfBoundsException: " + e.getMessage()); 252 | } 253 | } else { 254 | 255 | if (!mIsSizeChanging) { 256 | 257 | float eventX = event.getX(); 258 | float eventY = event.getY(); 259 | 260 | switch (event.getAction()) { 261 | case MotionEvent.ACTION_DOWN: 262 | mPenList.add(new Pen(color, width)); 263 | mPenList.get(mPenList.size() - 1).path.moveTo(eventX, eventY); 264 | return true; 265 | case MotionEvent.ACTION_MOVE: 266 | drawingStarted(); 267 | mPenList.get(mPenList.size() - 1).path.lineTo(eventX, eventY); 268 | break; 269 | case MotionEvent.ACTION_UP: 270 | drawingStopped(); 271 | break; 272 | default: 273 | return false; 274 | } 275 | } 276 | } 277 | } 278 | 279 | if(mStickers.size() > 0){ 280 | 281 | int newPositionX = (int)event.getX(); 282 | int newPositionY = (int)event.getY(); 283 | 284 | switch(event.getAction()){ 285 | 286 | case MotionEvent.ACTION_UP:{ 287 | Log.d(TAG, "touchEvent: reset sticker index."); 288 | 289 | resetSticker(newPositionX, newPositionY); 290 | 291 | break; 292 | } 293 | 294 | case MotionEvent.ACTION_POINTER_UP:{ 295 | Log.d(TAG, "touchEvent: reset sticker index."); 296 | 297 | resetSticker(newPositionX, newPositionY); 298 | 299 | break; 300 | } 301 | 302 | case MotionEvent.ACTION_MOVE:{ 303 | 304 | if(mSelectedStickerIndex != -1){ 305 | 306 | if(event.getPointerCount() > 1){ 307 | mIsStickerResizing = true; 308 | } 309 | else{ 310 | mIsStickerResizing = false; 311 | 312 | moveSticker(newPositionX, newPositionY); 313 | 314 | if(trashRect.contains(newPositionX, newPositionY)){ 315 | enlargeTrashIcon(); 316 | } 317 | else{ 318 | shrinkTrashIcon(); 319 | } 320 | } 321 | } 322 | 323 | if(mIsStickerResizing){ 324 | 325 | moveSticker(newPositionX, newPositionY); 326 | 327 | mScaleGestureDetector.onTouchEvent(event); 328 | } 329 | 330 | break; 331 | } 332 | 333 | case MotionEvent.ACTION_DOWN:{ 334 | for(int i = 0; i < mStickers.size(); i++){ 335 | if(mStickers.get(i).rect.contains(newPositionX, newPositionY)){ 336 | Log.d(TAG, "touchEvent: touched a STICKER."); 337 | mSelectedStickerIndex = i; 338 | mPrevStickerX = newPositionX; 339 | mPrevStickerY = newPositionY; 340 | 341 | dragStickerStarted(); 342 | 343 | break; // break the loop 344 | } 345 | } 346 | break; 347 | } 348 | 349 | } 350 | 351 | } 352 | 353 | invalidate(); 354 | return true; 355 | } 356 | 357 | private void dragStickerStarted(){ 358 | if(mHostActivity != null){ 359 | if(mHostActivity instanceof MainActivity){ 360 | ((MainActivity)mHostActivity).dragStickerStarted(); 361 | } 362 | } 363 | } 364 | 365 | private void dragStickerStopped(){ 366 | if(mHostActivity != null){ 367 | if(mHostActivity instanceof MainActivity){ 368 | ((MainActivity)mHostActivity).dragStickerStopped(); 369 | } 370 | } 371 | removeCircle(); 372 | } 373 | 374 | public void removeSticker(){ 375 | if(mSelectedStickerIndex != -1){ 376 | mStickers.remove(mSelectedStickerIndex); 377 | invalidate(); 378 | } 379 | } 380 | 381 | public void enlargeTrashIcon(){ 382 | if(mHostActivity instanceof MainActivity){ 383 | ((MainActivity)mHostActivity).setTrashIconSize(TRASH_ICON_ENLARGED_SIZE, TRASH_ICON_ENLARGED_SIZE); 384 | } 385 | } 386 | 387 | public void shrinkTrashIcon(){ 388 | if(mHostActivity instanceof MainActivity){ 389 | ((MainActivity)mHostActivity).setTrashIconSize(TRASH_ICON_NORMAL_SIZE, TRASH_ICON_NORMAL_SIZE); 390 | } 391 | } 392 | 393 | private void resetSticker(int newPositionX, int newPositionY){ 394 | if(trashRect.contains(newPositionX, newPositionY)){ 395 | removeSticker(); 396 | } 397 | 398 | mSelectedStickerIndex = -1; // reset the sticker index 399 | mIsStickerResizing = false; 400 | 401 | dragStickerStopped(); 402 | shrinkTrashIcon(); 403 | 404 | } 405 | 406 | private void moveSticker(int newPositionX, int newPositionY){ 407 | int dx = newPositionX - mPrevStickerX; 408 | int dy = newPositionY - mPrevStickerY; 409 | 410 | mPrevStickerX = newPositionX; 411 | mPrevStickerY = newPositionY; 412 | 413 | mStickers.get(mSelectedStickerIndex).x 414 | = mStickers.get(mSelectedStickerIndex).x + dx; 415 | 416 | mStickers.get(mSelectedStickerIndex).y 417 | = mStickers.get(mSelectedStickerIndex).y + dy; 418 | 419 | mStickers.get(mSelectedStickerIndex).adjustRect(); 420 | } 421 | 422 | 423 | public void addNewSticker(Drawable drawable){ 424 | if(mHostActivity != null){ 425 | if(mHostActivity instanceof MainActivity){ 426 | Log.d(TAG, "addNewSticker: adding new sticker to canvas."); 427 | Bitmap newStickerBitmap = drawableToBitmap(drawable); 428 | 429 | Sticker sticker = new Sticker(newStickerBitmap, drawable,0, 200); 430 | mStickers.add(sticker); 431 | invalidate(); 432 | } 433 | } 434 | } 435 | 436 | private void drawingStarted(){ 437 | if(mHostActivity != null){ 438 | if(mHostActivity instanceof MainActivity){ 439 | ((MainActivity)mHostActivity).hideStillshotWidgets(); 440 | } 441 | } 442 | } 443 | 444 | private void drawingStopped(){ 445 | if(mHostActivity != null){ 446 | if(mHostActivity instanceof MainActivity){ 447 | ((MainActivity)mHostActivity).showStillshotWidgets(); 448 | } 449 | } 450 | removeCircle(); 451 | } 452 | 453 | private void removeCircle(){ 454 | mCircle = null; 455 | } 456 | 457 | public void removeLastPath(){ 458 | if(mPenList.size() > 0){ 459 | mPenList.remove(mPenList.size() - 1); 460 | invalidate(); 461 | } 462 | } 463 | 464 | 465 | public void reset() { 466 | for (Pen pen : mPenList) { 467 | pen.path.reset(); 468 | } 469 | width = mMinWidth; 470 | mStickers.clear(); 471 | invalidate(); 472 | } 473 | 474 | public void setBrushColor(int color) { 475 | this.color = color; 476 | } 477 | 478 | public int getBrushColor(){ 479 | return color; 480 | } 481 | 482 | public void setWidth(float width) { 483 | this.width = width; 484 | } 485 | 486 | 487 | private void hideStatusBar() { 488 | 489 | if(mHostActivity != null){ 490 | View decorView = mHostActivity.getWindow().getDecorView(); 491 | // Hide the status bar. 492 | int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; 493 | decorView.setSystemUiVisibility(uiOptions); 494 | } 495 | 496 | } 497 | 498 | 499 | public void setDrawingIsEnabled(boolean bool){ 500 | mIsDrawingEnabled = bool; 501 | } 502 | 503 | private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 504 | 505 | @Override 506 | public boolean onScaleBegin(ScaleGestureDetector detector) { 507 | return true; 508 | } 509 | 510 | @Override 511 | public boolean onScale(ScaleGestureDetector detector) { 512 | float scaleFactor = detector.getScaleFactor(); 513 | 514 | if(scaleFactor > 1.011 || scaleFactor < 0.99) { 515 | 516 | if(mSelectedStickerIndex != -1){ 517 | mStickers.get(mSelectedStickerIndex).bitmap 518 | = resizeBitmap( 519 | mStickers.get(mSelectedStickerIndex).drawable, 520 | mStickers.get(mSelectedStickerIndex).bitmap, 521 | scaleFactor 522 | ); 523 | 524 | mIsStickerResizing = true; 525 | 526 | } 527 | else{ 528 | float prevWidth = width; 529 | if(scaleFactor > 1){ 530 | width += ( (SIZE_CHANGE_SPEED + (width * 0.05)) * scaleFactor ); 531 | } 532 | else{ 533 | width -= ( (SIZE_CHANGE_SPEED + (width * 0.05)) * scaleFactor ); 534 | } 535 | if ( width > mMaxWidth) { 536 | width = prevWidth; 537 | } 538 | else if (width < mMinWidth) { 539 | width = prevWidth; 540 | } 541 | } 542 | 543 | } 544 | 545 | 546 | return true; 547 | } 548 | } 549 | 550 | public static Bitmap resizeBitmap(Drawable drawable, Bitmap currentBitmap, float scale){ 551 | Bitmap bitmap = null; 552 | 553 | int newWidth = 0; 554 | int newHeight = 0; 555 | if(scale > 1){ 556 | newWidth = currentBitmap.getWidth() + (int)((currentBitmap.getWidth() * 0.04) * scale); 557 | newHeight = currentBitmap.getHeight() + (int)((currentBitmap.getHeight() * 0.04) * scale); 558 | } 559 | else{ 560 | newWidth = currentBitmap.getWidth() - (int)((currentBitmap.getHeight() * 0.04) * scale); 561 | newHeight = currentBitmap.getHeight() - (int)((currentBitmap.getHeight() * 0.04) * scale); 562 | } 563 | 564 | if(newWidth < MIN_STICKER_WIDTH){ 565 | newWidth = currentBitmap.getWidth(); 566 | } 567 | if(newHeight < MIN_STICKER_HEIGHT){ 568 | newHeight = currentBitmap.getHeight(); 569 | } 570 | 571 | if (drawable instanceof BitmapDrawable) { 572 | BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 573 | if(bitmapDrawable.getBitmap() != null) { 574 | return Bitmap.createScaledBitmap( 575 | bitmapDrawable.getBitmap(), 576 | newWidth, 577 | newHeight, 578 | false); 579 | } 580 | } 581 | 582 | if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 583 | bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel 584 | } 585 | else { 586 | bitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888); 587 | } 588 | 589 | Canvas canvas = new Canvas(bitmap); 590 | drawable.setBounds(0, 0, newWidth, newHeight); 591 | drawable.draw(canvas); 592 | return bitmap; 593 | } 594 | 595 | public static Bitmap drawableToBitmap (Drawable drawable) { 596 | Bitmap bitmap = null; 597 | 598 | if (drawable instanceof BitmapDrawable) { 599 | BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 600 | if(bitmapDrawable.getBitmap() != null) { 601 | return Bitmap.createScaledBitmap(bitmapDrawable.getBitmap(), STICKER_STARTING_WIDTH, STICKER_STARTING_HEIGHT, false); 602 | } 603 | } 604 | 605 | if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { 606 | bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel 607 | } 608 | else { 609 | bitmap = Bitmap.createBitmap(STICKER_STARTING_WIDTH, STICKER_STARTING_WIDTH, Bitmap.Config.ARGB_8888); 610 | } 611 | 612 | Canvas canvas = new Canvas(bitmap); 613 | drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); 614 | drawable.draw(canvas); 615 | return bitmap; 616 | } 617 | } 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/ICallback.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | /** 4 | * Created by User on 6/4/2018. 5 | */ 6 | 7 | public interface ICallback { 8 | 9 | void done(Exception e); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/IMainActivity.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.graphics.drawable.Drawable; 4 | 5 | /** 6 | * Created by User on 6/5/2018. 7 | */ 8 | 9 | public interface IMainActivity { 10 | 11 | void setCameraFrontFacing(); 12 | 13 | void setCameraBackFacing(); 14 | 15 | boolean isCameraFrontFacing(); 16 | 17 | boolean isCameraBackFacing(); 18 | 19 | void setFrontCameraId(String cameraId); 20 | 21 | void setBackCameraId(String cameraId); 22 | 23 | String getFrontCameraId(); 24 | 25 | String getBackCameraId(); 26 | 27 | void hideStatusBar(); 28 | 29 | void showStatusBar(); 30 | 31 | void hideStillshotWidgets(); 32 | 33 | void showStillshotWidgets(); 34 | 35 | void toggleViewStickersFragment(); 36 | 37 | void addSticker(Drawable sticker); 38 | 39 | void setTrashIconSize(int width, int height); 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/MainActivity.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Bundle; 9 | import android.support.annotation.NonNull; 10 | import android.support.design.widget.Snackbar; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.support.v4.app.FragmentTransaction; 13 | import android.support.v4.content.ContextCompat; 14 | import android.support.v7.app.AppCompatActivity; 15 | import android.util.DisplayMetrics; 16 | import android.util.Log; 17 | import android.view.View; 18 | import android.widget.Toast; 19 | 20 | public class MainActivity extends AppCompatActivity implements IMainActivity{ 21 | 22 | private static final String TAG = "MainActivity"; 23 | private static final int REQUEST_CODE = 1234; 24 | public static String CAMERA_POSITION_FRONT; 25 | public static String CAMERA_POSITION_BACK; 26 | public static String MAX_ASPECT_RATIO; 27 | 28 | //widgets 29 | 30 | //vars 31 | private boolean mPermissions; 32 | public String mCameraOrientation = "none"; // Front-facing or back-facing 33 | 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | 40 | init(); 41 | } 42 | 43 | private void startCamera2(){ 44 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 45 | transaction.replace(R.id.camera_container, Camera2Fragment.newInstance(), getString(R.string.fragment_camera2)); 46 | transaction.commit(); 47 | } 48 | 49 | private void init(){ 50 | if(mPermissions){ 51 | if(checkCameraHardware(this)){ 52 | 53 | // Open the Camera 54 | startCamera2(); 55 | } 56 | else{ 57 | showSnackBar("You need a camera to use this application", Snackbar.LENGTH_INDEFINITE); 58 | } 59 | } 60 | else{ 61 | verifyPermissions(); 62 | } 63 | } 64 | 65 | /** Check if this device has a camera */ 66 | private boolean checkCameraHardware(Context context) { 67 | if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ 68 | // this device has a camera 69 | return true; 70 | } else { 71 | // no camera on this device 72 | return false; 73 | } 74 | } 75 | 76 | public void verifyPermissions(){ 77 | Log.d(TAG, "verifyPermissions: asking user for permissions."); 78 | String[] permissions = { 79 | android.Manifest.permission.WRITE_EXTERNAL_STORAGE, 80 | Manifest.permission.CAMERA}; 81 | if (ContextCompat.checkSelfPermission(this.getApplicationContext(), 82 | permissions[0] ) == PackageManager.PERMISSION_GRANTED 83 | && ContextCompat.checkSelfPermission(this.getApplicationContext(), 84 | permissions[1] ) == PackageManager.PERMISSION_GRANTED) { 85 | mPermissions = true; 86 | init(); 87 | } else { 88 | ActivityCompat.requestPermissions( 89 | MainActivity.this, 90 | permissions, 91 | REQUEST_CODE 92 | ); 93 | } 94 | } 95 | 96 | @Override 97 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 98 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 99 | 100 | if(requestCode == REQUEST_CODE){ 101 | if(mPermissions){ 102 | init(); 103 | } 104 | else{ 105 | verifyPermissions(); 106 | } 107 | } 108 | } 109 | 110 | 111 | private void showSnackBar(final String text, final int length) { 112 | View view = this.findViewById(android.R.id.content).getRootView(); 113 | Snackbar.make(view, text, length).show(); 114 | } 115 | 116 | @Override 117 | public void setCameraFrontFacing() { 118 | Log.d(TAG, "setCameraFrontFacing: setting camera to front facing."); 119 | mCameraOrientation = CAMERA_POSITION_FRONT; 120 | } 121 | 122 | @Override 123 | public void setCameraBackFacing() { 124 | Log.d(TAG, "setCameraBackFacing: setting camera to back facing."); 125 | mCameraOrientation = CAMERA_POSITION_BACK; 126 | } 127 | 128 | @Override 129 | public void setFrontCameraId(String cameraId){ 130 | CAMERA_POSITION_FRONT = cameraId; 131 | } 132 | 133 | 134 | @Override 135 | public void setBackCameraId(String cameraId){ 136 | CAMERA_POSITION_BACK = cameraId; 137 | } 138 | 139 | @Override 140 | public boolean isCameraFrontFacing() { 141 | if(mCameraOrientation.equals(CAMERA_POSITION_FRONT)){ 142 | return true; 143 | } 144 | return false; 145 | } 146 | 147 | @Override 148 | public boolean isCameraBackFacing() { 149 | if(mCameraOrientation.equals(CAMERA_POSITION_BACK)){ 150 | return true; 151 | } 152 | return false; 153 | } 154 | 155 | @Override 156 | public String getBackCameraId(){ 157 | return CAMERA_POSITION_BACK; 158 | } 159 | 160 | @Override 161 | public String getFrontCameraId(){ 162 | return CAMERA_POSITION_FRONT; 163 | } 164 | 165 | @Override 166 | public void hideStatusBar() { 167 | 168 | View decorView = getWindow().getDecorView(); 169 | // Hide the status bar. 170 | int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; 171 | decorView.setSystemUiVisibility(uiOptions); 172 | } 173 | 174 | @Override 175 | public void showStatusBar() { 176 | 177 | View decorView = getWindow().getDecorView(); 178 | // Hide the status bar. 179 | int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 180 | decorView.setSystemUiVisibility(uiOptions); 181 | } 182 | 183 | @Override 184 | public void hideStillshotWidgets() { 185 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 186 | if (camera2Fragment != null) { 187 | if(camera2Fragment.isVisible()){ 188 | camera2Fragment.drawingStarted(); 189 | } 190 | } 191 | } 192 | 193 | @Override 194 | public void showStillshotWidgets() { 195 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 196 | if (camera2Fragment != null) { 197 | if(camera2Fragment.isVisible()){ 198 | camera2Fragment.drawingStopped(); 199 | } 200 | } 201 | } 202 | 203 | @Override 204 | public void toggleViewStickersFragment(){ 205 | 206 | ViewStickersFragment viewStickersFragment 207 | = (ViewStickersFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_view_stickers)); 208 | if (viewStickersFragment != null) { 209 | if(viewStickersFragment.isVisible()){ 210 | hideViewStickersFragment(viewStickersFragment); 211 | } 212 | else{ 213 | showViewStickersFragment(viewStickersFragment); 214 | } 215 | } 216 | else{ 217 | inflateViewStickersFragment(); 218 | } 219 | } 220 | 221 | private void hideViewStickersFragment(ViewStickersFragment fragment){ 222 | 223 | showStillshotWidgets(); 224 | 225 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 226 | transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up); 227 | transaction.hide(fragment); 228 | transaction.commit(); 229 | } 230 | 231 | private void showViewStickersFragment(ViewStickersFragment fragment){ 232 | 233 | hideStillshotWidgets(); 234 | 235 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 236 | transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up); 237 | transaction.show(fragment); 238 | transaction.commit(); 239 | } 240 | 241 | private void inflateViewStickersFragment(){ 242 | 243 | hideStillshotWidgets(); 244 | 245 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 246 | transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up); 247 | transaction.add(R.id.camera_container, ViewStickersFragment.newInstance(), getString(R.string.fragment_view_stickers)); 248 | transaction.commit(); 249 | } 250 | 251 | @Override 252 | public void addSticker(Drawable sticker){ 253 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 254 | if (camera2Fragment != null) { 255 | if(camera2Fragment.isVisible()){ 256 | camera2Fragment.addSticker(sticker); 257 | } 258 | } 259 | } 260 | 261 | @Override 262 | public void setTrashIconSize(int width, int height){ 263 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 264 | if (camera2Fragment != null) { 265 | if(camera2Fragment.isVisible()){ 266 | camera2Fragment.setTrashIconSize(width, height); 267 | } 268 | } 269 | } 270 | 271 | public void dragStickerStarted(){ 272 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 273 | if (camera2Fragment != null) { 274 | if(camera2Fragment.isVisible()){ 275 | camera2Fragment.dragStickerStarted(); 276 | } 277 | } 278 | } 279 | 280 | public void dragStickerStopped(){ 281 | Camera2Fragment camera2Fragment = (Camera2Fragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_camera2)); 282 | if (camera2Fragment != null) { 283 | if(camera2Fragment.isVisible()){ 284 | camera2Fragment.dragStickerStopped(); 285 | } 286 | } 287 | } 288 | } 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/ScalingTextureView.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | /** 4 | * Created by User on 5/21/2018. 5 | */ 6 | 7 | import android.annotation.TargetApi; 8 | import android.content.Context; 9 | import android.graphics.Matrix; 10 | import android.graphics.PointF; 11 | import android.os.Build; 12 | import android.util.AttributeSet; 13 | import android.util.Log; 14 | import android.view.MotionEvent; 15 | import android.view.ScaleGestureDetector; 16 | import android.view.TextureView; 17 | import android.view.View; 18 | 19 | import codingwithmitch.com.tabiancustomcamera.gestures.MoveGestureDetector; 20 | 21 | /** 22 | * A {@link TextureView} that can be adjusted to a specified aspect ratio. 23 | */ 24 | public class ScalingTextureView extends TextureView { 25 | 26 | private static final String TAG = "ScalingTextureView"; 27 | 28 | public int mRatioWidth = 0; 29 | public int mRatioHeight = 0; 30 | private int mScreenWidth = 0; 31 | private int mScreenHeight = 0; 32 | private String mRoundedScreenAspectRatio = ""; 33 | private String mRoundedPreviewAspectRatio = ""; 34 | 35 | private Matrix mMatrix; 36 | 37 | private ScaleGestureDetector mScaleDetector; 38 | 39 | private MoveGestureDetector mMoveDetector; 40 | 41 | // scaling 42 | public float mScaleFactor = 1.f; 43 | public float mScaleFactorX = 1.f; 44 | public float mScaleFactorY = 1.f; 45 | public float mWidthCorrection = 0f; 46 | float mScreenAspectRatio = 1f; 47 | float mPreviewAspectRatio = 1f; 48 | 49 | public float mImageCenterX = 0.f; 50 | 51 | public float mImageCenterY = 0.f; 52 | 53 | public float mFocusX = 0.f; 54 | 55 | public float mFocusY = 0.f; 56 | 57 | 58 | public ScalingTextureView(Context context) { 59 | this(context, null); 60 | init(context); 61 | } 62 | 63 | public ScalingTextureView(Context context, AttributeSet attrs) { 64 | this(context, attrs,0); 65 | init(context); 66 | } 67 | 68 | public ScalingTextureView(Context context, AttributeSet attrs, int defStyle) { 69 | super(context, attrs, defStyle); 70 | init(context); 71 | } 72 | 73 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 74 | public ScalingTextureView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 75 | super(context, attrs, defStyleAttr, defStyleRes); 76 | init(context); 77 | } 78 | 79 | /** 80 | * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio 81 | * calculated from the parameters. Note that the actual sizes of parameters don't matter, that 82 | * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. 83 | * 84 | * @param width Relative horizontal size 85 | * @param height Relative vertical size 86 | */ 87 | public void setAspectRatio(int width, int height, int screenWidth, int screenHeight) { 88 | if (width < 0 || height < 0) { 89 | throw new IllegalArgumentException("Size cannot be negative."); 90 | } 91 | mRatioWidth = width; 92 | mRatioHeight = height; 93 | requestLayout(); 94 | mScreenWidth = screenWidth; 95 | mScreenHeight = screenHeight; 96 | 97 | mScreenAspectRatio = (float)mScreenHeight / (float)mScreenWidth; 98 | mPreviewAspectRatio = (float)mRatioHeight / (float)mRatioWidth; 99 | mRoundedScreenAspectRatio = String.format("%.2f", mScreenAspectRatio); 100 | mRoundedPreviewAspectRatio = String.format("%.2f", mPreviewAspectRatio); 101 | getWidthCorrection(); 102 | } 103 | 104 | @Override 105 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 106 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 107 | int width = MeasureSpec.getSize(widthMeasureSpec); 108 | int height = MeasureSpec.getSize(heightMeasureSpec); 109 | if (0 == mRatioWidth || 0 == mRatioHeight) { 110 | setMeasuredDimension(width, height); 111 | } 112 | setMeasuredDimension(mScreenWidth, mScreenHeight); 113 | 114 | } 115 | 116 | 117 | private void init(Context context) { 118 | 119 | mMatrix = new Matrix(); 120 | 121 | // Setup Gesture Detectors 122 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 123 | 124 | mMoveDetector = new MoveGestureDetector(context, new MoveListener()); 125 | } 126 | 127 | private void getWidthCorrection(){ 128 | String roundedScreenAspectRatio = String.format("%.2f", mScreenAspectRatio); 129 | String roundedPreviewAspectRatio = String.format("%.2f", mPreviewAspectRatio); 130 | if(!roundedPreviewAspectRatio.equals(roundedScreenAspectRatio) ){ 131 | 132 | float scaleFactor = (mScreenAspectRatio / mPreviewAspectRatio); 133 | Log.d(TAG, "configureTransform: scale factor: " + scaleFactor); 134 | 135 | mWidthCorrection = (((float)mScreenWidth * scaleFactor) - mScreenWidth) / 2; 136 | Log.d(TAG, "getWidthCorrection: width correction: " + mWidthCorrection); 137 | } 138 | } 139 | 140 | 141 | public boolean onTouch(MotionEvent motionEvent) { 142 | 143 | mScaleDetector.onTouchEvent(motionEvent); 144 | 145 | mMoveDetector.onTouchEvent(motionEvent); 146 | 147 | if(mScaleFactor > 1.011111 || mScaleFactor < 0.99999) { 148 | 149 | mMatrix.reset(); 150 | 151 | Log.d(TAG, "onTouch: scale factor: " + mScaleFactor); 152 | 153 | mScaleFactorY = mScaleFactor; 154 | mScaleFactorX = mScaleFactor; 155 | 156 | 157 | if(!mRoundedPreviewAspectRatio.equals(mRoundedScreenAspectRatio) ){ 158 | 159 | mScaleFactorX *= (mScreenAspectRatio / mPreviewAspectRatio); 160 | Log.d(TAG, "configureTransform: scale factor: " + mScaleFactorX); 161 | } 162 | 163 | float scaledImageCenterX = (getWidth() * mScaleFactorX) / 2; 164 | 165 | float scaledImageCenterY = (getHeight() * mScaleFactorY) / 2; 166 | 167 | 168 | mMatrix.postScale(mScaleFactorX, mScaleFactorY); 169 | 170 | float dx = mImageCenterX - scaledImageCenterX; 171 | 172 | float dy = mImageCenterY - scaledImageCenterY; 173 | 174 | Log.d(TAG, "onTouch: dx: " + dx + ", dy: " + dy); 175 | 176 | 177 | // BOUNDARY 1: Right 178 | if (dx < (getWidth() - (((float)mScreenWidth - mWidthCorrection) * mScaleFactorX))) { 179 | 180 | dx = (getWidth() - (((float)mScreenWidth - mWidthCorrection) * mScaleFactorX)); 181 | 182 | mImageCenterX = dx + scaledImageCenterX; // reverse the changes 183 | 184 | } 185 | 186 | //BOUNDARY 2: Bottom 187 | if (dy < (getHeight() - ((float)mScreenHeight * mScaleFactorY))) { 188 | 189 | dy = (getHeight() - ((float)mScreenHeight * mScaleFactorY)); 190 | 191 | mImageCenterY = dy + scaledImageCenterY; 192 | 193 | } 194 | 195 | 196 | // BOUNDARY 3: Left 197 | if (dx > -mWidthCorrection) { 198 | 199 | dx = -mWidthCorrection; 200 | 201 | mImageCenterX = dx + scaledImageCenterX; 202 | } 203 | 204 | 205 | // BOUNDARY 4: Top 206 | if (dy > 0) { 207 | 208 | dy = 0; 209 | 210 | mImageCenterY = dy + scaledImageCenterY; 211 | } 212 | 213 | mMatrix.postTranslate(dx, dy); 214 | 215 | setTransform(mMatrix); 216 | 217 | setAlpha(1); 218 | 219 | mFocusX = -1 * (dx / mScaleFactorX); 220 | mFocusY = -1 * (dy / mScaleFactorY); 221 | } 222 | return true; // indicate event was handled 223 | 224 | } 225 | 226 | 227 | private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 228 | @Override 229 | public boolean onScale(ScaleGestureDetector detector) { 230 | 231 | mScaleFactor *= detector.getScaleFactor(); // scale change since previous event 232 | 233 | // Don't let the object get too small or too large. 234 | mScaleFactor = Math.max(1.f, Math.min(mScaleFactor, 4.0f)); 235 | 236 | return true; 237 | } 238 | } 239 | 240 | private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener { 241 | @Override 242 | public boolean onMove(MoveGestureDetector detector) { 243 | 244 | PointF d = detector.getFocusDelta(); 245 | 246 | mImageCenterX += d.x; 247 | 248 | mImageCenterY += d.y; 249 | 250 | Log.d(TAG, "onMove: image center x: " + mImageCenterX); 251 | Log.d(TAG, "onMove: image canter y: " + mImageCenterY); 252 | 253 | return true; 254 | } 255 | } 256 | 257 | public void resetScale(){ 258 | mScaleFactor = 1.0f; 259 | mScaleFactorX = 1f; 260 | mScaleFactorY = 1f; 261 | mImageCenterX = mRatioWidth / 2; 262 | mImageCenterX = mRatioHeight / 2; 263 | mFocusX = 0f; 264 | mFocusY = 0f; 265 | } 266 | 267 | } 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/StickerAdapter.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.annotation.NonNull; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.Log; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | 13 | import com.bumptech.glide.Glide; 14 | 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * Created by User on 5/24/2018. 19 | */ 20 | 21 | public class StickerAdapter extends RecyclerView.Adapter { 22 | 23 | private static final String TAG = "StickerAdapter"; 24 | 25 | private ArrayList mStickers = new ArrayList<>(); 26 | private RecyclerViewClickListener mClickListener; 27 | private Context mContext; 28 | 29 | 30 | public StickerAdapter(Context context, ArrayList stickers, RecyclerViewClickListener clickListener) { 31 | mStickers = stickers; 32 | mClickListener = clickListener; 33 | mContext = context; 34 | } 35 | 36 | 37 | @NonNull 38 | @Override 39 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 40 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_sticker_list_item, parent, false); 41 | final ViewHolder holder = new ViewHolder(view, mClickListener); 42 | return holder; 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 47 | 48 | Glide.with(mContext) 49 | .load(mStickers.get(position)) 50 | .into(((ViewHolder)holder).image); 51 | } 52 | 53 | @Override 54 | public int getItemCount() { 55 | return mStickers.size(); 56 | } 57 | 58 | 59 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ 60 | 61 | ImageView image; 62 | RecyclerViewClickListener clickListener; 63 | 64 | public ViewHolder(View itemView, RecyclerViewClickListener clickListener) { 65 | super(itemView); 66 | image = itemView.findViewById(R.id.sticker_image); 67 | this.clickListener = clickListener; 68 | 69 | itemView.setOnClickListener(this); 70 | } 71 | 72 | @Override 73 | public void onClick(View view) { 74 | if(clickListener != null){ 75 | Log.d(TAG, "onClick: clicked."); 76 | clickListener.onStickerClicked(getAdapterPosition()); 77 | } 78 | } 79 | } 80 | 81 | public interface RecyclerViewClickListener{ 82 | void onStickerClicked(int position); 83 | } 84 | } 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/Utility.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.util.Log; 4 | import android.util.Size; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.Comparator; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by User on 5/29/2018. 13 | */ 14 | 15 | public class Utility { 16 | 17 | /** 18 | * Compares two {@code Size}s based on their areas. 19 | */ 20 | static class CompareSizesByArea implements Comparator { 21 | 22 | public static CompareSizesByArea newInstance(){ 23 | return new CompareSizesByArea(); 24 | } 25 | @Override 26 | public int compare(Size lhs, Size rhs) { 27 | // We cast here to ensure the multiplications won't overflow 28 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - 29 | (long) rhs.getWidth() * rhs.getHeight()); 30 | } 31 | 32 | } 33 | 34 | /** 35 | * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that 36 | * is at least as large as the respective texture view size, and that is at most as large as the 37 | * respective max size, and whose aspect ratio matches with the specified value. If such size 38 | * doesn't exist, choose the largest one that is at most as large as the respective max size, 39 | * and whose aspect ratio matches with the specified value. 40 | * 41 | * @param choices The list of sizes that the camera supports for the intended output 42 | * class 43 | * @param textureViewWidth The width of the texture view relative to sensor coordinate 44 | * @param textureViewHeight The height of the texture view relative to sensor coordinate 45 | * @param maxWidth The maximum width that can be chosen 46 | * @param maxHeight The maximum height that can be chosen 47 | * @param aspectRatio The aspect ratio 48 | * @return The optimal {@code Size}, or an arbitrary one if none were big enough 49 | */ 50 | static Size chooseOptimalSize(Size[] choices, int textureViewWidth, 51 | int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { 52 | 53 | // Collect the supported resolutions that are at least as big as the preview Surface 54 | List bigEnough = new ArrayList<>(); 55 | // Collect the supported resolutions that are smaller than the preview Surface 56 | List notBigEnough = new ArrayList<>(); 57 | int w = aspectRatio.getWidth(); 58 | int h = aspectRatio.getHeight(); 59 | for (Size option : choices) { 60 | // Log.d(TAG, "chooseOptimalSize: w: " + option.getWidth() + ", h: " + option.getHeight()); 61 | 62 | if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && 63 | option.getHeight() == option.getWidth() * h / w) { 64 | if (option.getWidth() >= textureViewWidth && 65 | option.getHeight() >= textureViewHeight) { 66 | bigEnough.add(option); 67 | } else { 68 | notBigEnough.add(option); 69 | } 70 | } 71 | } 72 | 73 | // Pick the smallest of those big enough. If there is no one big enough, pick the 74 | // largest of those not big enough. 75 | if (bigEnough.size() > 0) { 76 | return Collections.min(bigEnough, new CompareSizesByArea()); 77 | } else if (notBigEnough.size() > 0) { 78 | return Collections.max(notBigEnough, new CompareSizesByArea()); 79 | } else { 80 | return choices[0]; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/VerticalSlideColorPicker.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.LinearGradient; 9 | import android.graphics.Paint; 10 | import android.graphics.Path; 11 | import android.graphics.RectF; 12 | import android.graphics.Shader; 13 | import android.util.AttributeSet; 14 | import android.view.MotionEvent; 15 | import android.view.View; 16 | 17 | 18 | public class VerticalSlideColorPicker extends View { 19 | 20 | private Paint paint; 21 | private Paint strokePaint; 22 | private Path path; 23 | private Bitmap bitmap; 24 | private int viewWidth; 25 | private int viewHeight; 26 | private int centerX; 27 | private float colorPickerRadius; 28 | private OnColorChangeListener onColorChangeListener; 29 | private RectF colorPickerBody; 30 | private float selectorYPos; 31 | private int borderColor; 32 | private float borderWidth; 33 | private int[] colors; 34 | private boolean cacheBitmap = true; 35 | 36 | public VerticalSlideColorPicker(Context context) { 37 | super(context); 38 | init(); 39 | } 40 | 41 | public VerticalSlideColorPicker(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | TypedArray a = context.getTheme().obtainStyledAttributes( 44 | attrs, 45 | R.styleable.VerticalSlideColorPicker, 46 | 0, 0); 47 | 48 | try { 49 | borderColor = a.getColor(R.styleable.VerticalSlideColorPicker_borderColor, Color.WHITE); 50 | borderWidth = a.getDimension(R.styleable.VerticalSlideColorPicker_borderWidth, 10f); 51 | int colorsResourceId = a.getResourceId(R.styleable.VerticalSlideColorPicker_colors, R.array.default_colors); 52 | colors = a.getResources().getIntArray(colorsResourceId); 53 | } finally { 54 | a.recycle(); 55 | } 56 | init(); 57 | } 58 | 59 | public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr) { 60 | super(context, attrs, defStyleAttr); 61 | init(); 62 | } 63 | 64 | public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 65 | super(context, attrs, defStyleAttr, defStyleRes); 66 | init(); 67 | } 68 | 69 | private void init() { 70 | setWillNotDraw(false); 71 | paint = new Paint(); 72 | paint.setStyle(Paint.Style.FILL); 73 | paint.setAntiAlias(true); 74 | 75 | path = new Path(); 76 | 77 | strokePaint = new Paint(); 78 | strokePaint.setStyle(Paint.Style.STROKE); 79 | strokePaint.setColor(borderColor); 80 | strokePaint.setAntiAlias(true); 81 | strokePaint.setStrokeWidth(borderWidth); 82 | 83 | setDrawingCacheEnabled(true); 84 | 85 | } 86 | 87 | @Override 88 | protected void onDraw(Canvas canvas) { 89 | super.onDraw(canvas); 90 | 91 | path.addCircle(centerX, borderWidth + colorPickerRadius, colorPickerRadius, Path.Direction.CW); 92 | path.addRect(colorPickerBody, Path.Direction.CW); 93 | path.addCircle(centerX, viewHeight - (borderWidth + colorPickerRadius), colorPickerRadius, Path.Direction.CW); 94 | 95 | canvas.drawPath(path, strokePaint); 96 | canvas.drawPath(path, paint); 97 | 98 | if (cacheBitmap) { 99 | bitmap = getDrawingCache(); 100 | cacheBitmap = false; 101 | invalidate(); 102 | } else { 103 | canvas.drawLine(colorPickerBody.left, selectorYPos, colorPickerBody.right, selectorYPos, strokePaint); 104 | } 105 | } 106 | 107 | @Override 108 | public boolean onTouchEvent(MotionEvent event) { 109 | 110 | float yPos = Math.min(event.getY(), colorPickerBody.bottom); 111 | yPos = Math.max(colorPickerBody.top, yPos); 112 | 113 | selectorYPos = yPos; 114 | int selectedColor = bitmap.getPixel(viewWidth/2, (int) selectorYPos); 115 | 116 | if (onColorChangeListener != null) { 117 | onColorChangeListener.onColorChange(selectedColor); 118 | } 119 | 120 | invalidate(); 121 | 122 | return true; 123 | } 124 | 125 | @Override 126 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 127 | super.onSizeChanged(w, h, oldw, oldh); 128 | 129 | viewWidth = w; 130 | viewHeight = h; 131 | 132 | centerX = viewWidth/2; 133 | colorPickerRadius = (viewWidth/2) - borderWidth; 134 | 135 | colorPickerBody = new RectF(centerX - colorPickerRadius, borderWidth + colorPickerRadius, centerX + colorPickerRadius, viewHeight - (borderWidth + colorPickerRadius)); 136 | 137 | LinearGradient gradient = new LinearGradient(0, colorPickerBody.top, 0, colorPickerBody.bottom, colors, null, Shader.TileMode.CLAMP); 138 | paint.setShader(gradient); 139 | 140 | resetToDefault(); 141 | } 142 | 143 | public void setBorderColor(int borderColor) { 144 | this.borderColor = borderColor; 145 | invalidate(); 146 | } 147 | 148 | public void setBorderWidth(float borderWidth) { 149 | this.borderWidth = borderWidth; 150 | invalidate(); 151 | } 152 | 153 | public void setColors(int[] colors) { 154 | this.colors = colors; 155 | cacheBitmap = true; 156 | invalidate(); 157 | } 158 | 159 | public void resetToDefault() { 160 | selectorYPos = borderWidth + colorPickerRadius; 161 | 162 | if (onColorChangeListener != null) { 163 | onColorChangeListener.onColorChange(Color.TRANSPARENT); 164 | } 165 | 166 | invalidate(); 167 | } 168 | 169 | public void setOnColorChangeListener(OnColorChangeListener onColorChangeListener) { 170 | this.onColorChangeListener = onColorChangeListener; 171 | } 172 | 173 | public interface OnColorChangeListener { 174 | 175 | void onColorChange(int selectedColor); 176 | } 177 | } -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/ViewStickersFragment.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.app.Fragment; 9 | import android.support.v7.widget.GridLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | 16 | import java.util.ArrayList; 17 | 18 | /** 19 | * Created by User on 5/24/2018. 20 | */ 21 | 22 | public class ViewStickersFragment extends Fragment implements 23 | StickerAdapter.RecyclerViewClickListener, 24 | View.OnClickListener 25 | { 26 | 27 | private static final String TAG = "ViewStickersFragment"; 28 | private static final int NUM_COLUMNS = 3; 29 | 30 | //widgets 31 | private RecyclerView mRecyclerView; 32 | 33 | //vars 34 | private ArrayList mStickers = new ArrayList<>(); 35 | private IMainActivity mIMainActivity; 36 | private StickerAdapter mStickerAdapter; 37 | 38 | public static ViewStickersFragment newInstance() { 39 | return new ViewStickersFragment(); 40 | } 41 | 42 | @Nullable 43 | @Override 44 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 45 | View view = inflater.inflate(R.layout.fragment_view_stickers, container, false); 46 | view.findViewById(R.id.init_sticker_icon).setOnClickListener(this); 47 | mRecyclerView = view.findViewById(R.id.recycler_view); 48 | 49 | getStickers(); 50 | initRecyclerView(); 51 | 52 | return view; 53 | } 54 | 55 | private void initRecyclerView(){ 56 | if(mStickerAdapter == null){ 57 | mStickerAdapter = new StickerAdapter(getActivity(), mStickers , this); 58 | } 59 | mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), NUM_COLUMNS)); 60 | mRecyclerView.setAdapter(mStickerAdapter); 61 | } 62 | 63 | private void getStickers(){ 64 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.astonished_face_emoji)); 65 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.cry_emoji)); 66 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.nerd_emoji)); 67 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.sad_emoji)); 68 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.slightly_smiling_face_emoji)); 69 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.smiley_face_emoji)); 70 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.smiley_face_tightly_closed_eyes_emoji)); 71 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.smiley_smiling_eyes_emoji)); 72 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.smiley_with_sweat_emoji)); 73 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.smirking_emoji)); 74 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.sunglasses_emoji)); 75 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.tears_of_joy_emoji)); 76 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.unamused_emoji)); 77 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.upside_down_face_emoji)); 78 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.ic_undo_small)); 79 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.evil_monkey_emoji)); 80 | mStickers.add(getActivity().getResources().getDrawable(R.drawable.penguin_emoji)); 81 | } 82 | 83 | @Override 84 | public void onStickerClicked(int position) { 85 | mIMainActivity.addSticker(mStickers.get(position)); 86 | 87 | mIMainActivity.toggleViewStickersFragment(); 88 | } 89 | 90 | @Override 91 | public void onClick(View view) { 92 | 93 | switch (view.getId()){ 94 | 95 | case R.id.init_sticker_icon:{ 96 | mIMainActivity.toggleViewStickersFragment(); 97 | break; 98 | } 99 | } 100 | } 101 | 102 | @Override 103 | public void onAttach(Context context) { 104 | super.onAttach(context); 105 | try{ 106 | mIMainActivity = (IMainActivity) getActivity(); 107 | }catch (ClassCastException e){ 108 | Log.e(TAG, "onAttach: ClassCastException: " + e.getMessage() ); 109 | } 110 | } 111 | } 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/gestures/BaseGestureDetector.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera.gestures; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | 7 | /** 8 | * @author Almer Thie (code.almeros.com) 9 | * Copyright (c) 2013, Almer Thie (code.almeros.com) 10 | * 11 | * All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 14 | * 15 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 16 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 17 | * in the documentation and/or other materials provided with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 20 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 22 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 23 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | */ 27 | 28 | 29 | public abstract class BaseGestureDetector { 30 | 31 | private static final String TAG = "BaseGestureDetector"; 32 | 33 | protected final Context mContext; 34 | protected boolean mGestureInProgress; 35 | 36 | protected MotionEvent mPrevEvent; 37 | protected MotionEvent mCurrEvent; 38 | 39 | protected float mCurrPressure; 40 | protected float mPrevPressure; 41 | protected long mTimeDelta; 42 | 43 | 44 | /** 45 | * This value is the threshold ratio between the previous combined pressure 46 | * and the current combined pressure. When pressure decreases rapidly 47 | * between events the position values can often be imprecise, as it usually 48 | * indicates that the user is in the process of lifting a pointer off of the 49 | * device. This value was tuned experimentally. 50 | */ 51 | protected static final float PRESSURE_THRESHOLD = 0.67f; 52 | 53 | 54 | public BaseGestureDetector(Context context) { 55 | mContext = context; 56 | } 57 | 58 | /** 59 | * All gesture detectors need to be called through this method to be able to 60 | * detect gestures. This method delegates work to handler methods 61 | * (handleStartProgressEvent, handleInProgressEvent) implemented in 62 | * extending classes. 63 | * 64 | * @param event 65 | * @return 66 | */ 67 | public boolean onTouchEvent(MotionEvent event){ 68 | final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; 69 | if (!mGestureInProgress) { 70 | handleStartProgressEvent(actionCode, event); 71 | } else { 72 | handleInProgressEvent(actionCode, event); 73 | } 74 | return true; 75 | } 76 | 77 | /** 78 | * Called when the current event occurred when NO gesture is in progress 79 | * yet. The handling in this implementation may set the gesture in progress 80 | * (via mGestureInProgress) or out of progress 81 | * @param actionCode 82 | * @param event 83 | */ 84 | protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event); 85 | 86 | /** 87 | * Called when the current event occurred when a gesture IS in progress. The 88 | * handling in this implementation may set the gesture out of progress (via 89 | * mGestureInProgress). 90 | * @param event 91 | */ 92 | protected abstract void handleInProgressEvent(int actionCode, MotionEvent event); 93 | 94 | 95 | protected void updateStateByEvent(MotionEvent curr){ 96 | final MotionEvent prev = mPrevEvent; 97 | 98 | // Reset mCurrEvent 99 | if (mCurrEvent != null) { 100 | mCurrEvent.recycle(); 101 | mCurrEvent = null; 102 | } 103 | 104 | try{ 105 | mCurrEvent = MotionEvent.obtain(curr); 106 | 107 | 108 | // Delta time 109 | mTimeDelta = curr.getEventTime() - prev.getEventTime(); 110 | 111 | // Pressure 112 | mCurrPressure = curr.getPressure(curr.getActionIndex()); 113 | mPrevPressure = prev.getPressure(prev.getActionIndex()); 114 | }catch (NullPointerException e){ 115 | Log.e(TAG, "updateStateByEvent: NullPointerException: " + e.getMessage() ); 116 | } 117 | 118 | } 119 | 120 | protected void resetState() { 121 | if (mPrevEvent != null) { 122 | mPrevEvent.recycle(); 123 | mPrevEvent = null; 124 | } 125 | if (mCurrEvent != null) { 126 | mCurrEvent.recycle(); 127 | mCurrEvent = null; 128 | } 129 | mGestureInProgress = false; 130 | } 131 | 132 | 133 | /** 134 | * Returns {@code true} if a gesture is currently in progress. 135 | * @return {@code true} if a gesture is currently in progress, {@code false} otherwise. 136 | */ 137 | public boolean isInProgress() { 138 | return mGestureInProgress; 139 | } 140 | 141 | /** 142 | * Return the time difference in milliseconds between the previous accepted 143 | * GestureDetector event and the current GestureDetector event. 144 | * 145 | * @return Time difference since the last move event in milliseconds. 146 | */ 147 | public long getTimeDelta() { 148 | return mTimeDelta; 149 | } 150 | 151 | /** 152 | * Return the event time of the current GestureDetector event being 153 | * processed. 154 | * 155 | * @return Current GestureDetector event time in milliseconds. 156 | */ 157 | public long getEventTime() { 158 | return mCurrEvent.getEventTime(); 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/tabiancustomcamera/gestures/MoveGestureDetector.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera.gestures; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.util.Log; 6 | import android.view.MotionEvent; 7 | 8 | /** 9 | * @author Almer Thie (code.almeros.com) 10 | * Copyright (c) 2013, Almer Thie (code.almeros.com) 11 | *

12 | * All rights reserved. 13 | *

14 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 15 | *

16 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 17 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 18 | * in the documentation and/or other materials provided with the distribution. 19 | *

20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 23 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 26 | * OF SUCH DAMAGE. 27 | */ 28 | public class MoveGestureDetector extends BaseGestureDetector { 29 | 30 | private static final String TAG = "MoveGestureDetector"; 31 | 32 | /** 33 | * Listener which must be implemented which is used by MoveGestureDetector 34 | * to perform callbacks to any implementing class which is registered to a 35 | * MoveGestureDetector via the constructor. 36 | * 37 | * @see MoveGestureDetector.SimpleOnMoveGestureListener 38 | */ 39 | public interface OnMoveGestureListener { 40 | public boolean onMove(MoveGestureDetector detector); 41 | 42 | public boolean onMoveBegin(MoveGestureDetector detector); 43 | 44 | public void onMoveEnd(MoveGestureDetector detector); 45 | } 46 | 47 | /** 48 | * Helper class which may be extended and where the methods may be 49 | * implemented. This way it is not necessary to implement all methods 50 | * of OnMoveGestureListener. 51 | */ 52 | public static class SimpleOnMoveGestureListener implements OnMoveGestureListener { 53 | public boolean onMove(MoveGestureDetector detector) { 54 | return false; 55 | } 56 | 57 | public boolean onMoveBegin(MoveGestureDetector detector) { 58 | return true; 59 | } 60 | 61 | public void onMoveEnd(MoveGestureDetector detector) { 62 | // Do nothing, overridden implementation may be used 63 | } 64 | } 65 | 66 | private static final PointF FOCUS_DELTA_ZERO = new PointF(); 67 | 68 | private final OnMoveGestureListener mListener; 69 | 70 | private PointF mCurrFocusInternal; 71 | private PointF mPrevFocusInternal; 72 | private PointF mFocusExternal = new PointF(); 73 | private PointF mFocusDeltaExternal = new PointF(); 74 | 75 | public MoveGestureDetector(Context context, OnMoveGestureListener listener) { 76 | super(context); 77 | mListener = listener; 78 | } 79 | 80 | @Override 81 | protected void handleStartProgressEvent(int actionCode, MotionEvent event) { 82 | 83 | switch (actionCode) { 84 | 85 | case MotionEvent.ACTION_DOWN: 86 | 87 | resetState(); // In case we missed an UP/CANCEL event 88 | 89 | mPrevEvent = MotionEvent.obtain(event); 90 | mTimeDelta = 0; 91 | 92 | updateStateByEvent(event); 93 | break; 94 | 95 | case MotionEvent.ACTION_MOVE: 96 | 97 | mGestureInProgress = mListener.onMoveBegin(this); 98 | break; 99 | } 100 | } 101 | 102 | @Override 103 | protected void handleInProgressEvent(int actionCode, MotionEvent event) { 104 | switch (actionCode) { 105 | 106 | case MotionEvent.ACTION_UP: 107 | 108 | case MotionEvent.ACTION_CANCEL: 109 | 110 | mListener.onMoveEnd(this); 111 | resetState(); 112 | break; 113 | 114 | case MotionEvent.ACTION_MOVE: 115 | 116 | updateStateByEvent(event); 117 | 118 | // Only accept the event if our relative pressure is within 119 | // a certain limit. This can help filter shaky data as a 120 | // finger is lifted. 121 | if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { 122 | final boolean updatePrevious = mListener.onMove(this); 123 | if (updatePrevious) { 124 | mPrevEvent.recycle(); 125 | mPrevEvent = MotionEvent.obtain(event); 126 | } 127 | } 128 | break; 129 | } 130 | } 131 | 132 | protected void updateStateByEvent(MotionEvent curr) { 133 | super.updateStateByEvent(curr); 134 | 135 | try{ 136 | final MotionEvent prev = mPrevEvent; 137 | 138 | // Focus internal 139 | mCurrFocusInternal = determineFocalPoint(curr); 140 | mPrevFocusInternal = determineFocalPoint(prev); 141 | 142 | // Focus external 143 | // - Prevent skipping of focus delta when a finger is added or removed 144 | boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount(); 145 | mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y); 146 | 147 | // - Don't directly use mFocusInternal (or skipping will occur). Add 148 | // unskipped delta values to mFocusExternal instead. 149 | mFocusExternal.x += mFocusDeltaExternal.x; 150 | mFocusExternal.y += mFocusDeltaExternal.y; 151 | 152 | }catch (NullPointerException e){ 153 | Log.e(TAG, "updateStateByEvent: NullPointerException: " + e.getMessage() ); 154 | } 155 | } 156 | 157 | /** 158 | * Determine (multi)finger focal point (a.k.a. center point between all fingers) 159 | * 160 | * @param e detected motion event 161 | * @return PointF focal point 162 | */ 163 | private PointF determineFocalPoint(MotionEvent e) { 164 | // Number of fingers on screen 165 | final int pCount = e.getPointerCount(); 166 | float x = 0f; 167 | float y = 0f; 168 | 169 | for (int i = 0; i < pCount; i++) { 170 | x += e.getX(i); 171 | y += e.getY(i); 172 | } 173 | 174 | return new PointF(x / pCount, y / pCount); 175 | } 176 | 177 | public float getFocusX() { 178 | return mFocusExternal.x; 179 | } 180 | 181 | public float getFocusY() { 182 | return mFocusExternal.y; 183 | } 184 | 185 | public PointF getFocusDelta() { 186 | return mFocusDeltaExternal; 187 | } 188 | 189 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_flash_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_flash_auto.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_flash_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_flash_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_flash_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_flash_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pen_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_pen_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_save_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_save_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_sticker_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_sticker_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_switch_camera_orient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_switch_camera_orient.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_undo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-hdpi/ic_undo_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_flash_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_flash_auto.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_flash_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_flash_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_flash_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_flash_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pen_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_pen_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_save_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_save_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_sticker_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_sticker_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_switch_camera_orient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_switch_camera_orient.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_undo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-mdpi/ic_undo_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_flash_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_flash_auto.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_flash_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_flash_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_flash_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_flash_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pen_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_pen_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_save_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_save_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_sticker_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_sticker_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_switch_camera_orient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_switch_camera_orient.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_undo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xhdpi/ic_undo_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_flash_auto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_flash_auto.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_flash_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_flash_off.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_flash_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_flash_on.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pen_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_pen_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_save_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_save_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_sticker_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_sticker_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_switch_camera_orient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_switch_camera_orient.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_undo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable-xxhdpi/ic_undo_small.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/astonished_face_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/astonished_face_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/cam_action_capture.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cam_action_stillshot.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cam_circle_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cam_grey_circle_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_opaque.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/circle_white.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cry_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/cry_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/evil_monkey_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/evil_monkey_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/nerd_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/nerd_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/penguin_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/penguin_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/sad_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/sad_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/slightly_smiling_face_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/slightly_smiling_face_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/smiley_face_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/smiley_face_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/smiley_face_tightly_closed_eyes_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/smiley_face_tightly_closed_eyes_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/smiley_smiling_eyes_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/smiley_smiling_eyes_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/smiley_with_sweat_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/smiley_with_sweat_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/smirking_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/smirking_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/sunglasses_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/sunglasses_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/tears_of_joy_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/tears_of_joy_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/unamused_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/unamused_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/upside_down_face_emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/upside_down_face_emoji.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/x_white_border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/drawable/x_white_border.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_camera2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 18 | 19 | 20 | 21 | 31 | 32 | 42 | 43 | 44 | 45 | 46 | 47 | 57 | 58 | 64 | 65 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 92 | 93 | 100 | 101 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 123 | 124 | 125 | 129 | 130 | 131 | 142 | 143 | 148 | 149 | 150 | 151 | 152 | 153 | 164 | 165 | 176 | 177 | 178 | 179 | 180 | 192 | 193 | 204 | 205 | 206 | 207 | 208 | 209 | 220 | 221 | 222 | 228 | 229 | 238 | 239 | 240 | 241 | 242 | 243 | 255 | 256 | 264 | 265 | 266 | 267 | 275 | 276 | 281 | 282 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 311 | 312 | 317 | 318 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_view_stickers.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 26 | 27 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_sticker_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/array.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF007F 4 | #FF0000 5 | #FF7F00 6 | #FFFF00 7 | #7FFF00 8 | #00FF00 9 | #00FF7F 10 | #00FFFF 11 | #007FFF 12 | #0000FF 13 | #7F00FF 14 | #FF00FF 15 | #000000 16 | #ffffff 17 | 18 | 19 | @color/white 20 | @color/bright_pink 21 | @color/red 22 | @color/orange 23 | @color/yellow 24 | @color/chartreuse 25 | @color/green 26 | @color/spring_green 27 | @color/cyan 28 | @color/azure 29 | @color/blue 30 | @color/violet 31 | @color/magenta 32 | @color/black 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #af0a0a 8 | #c40909 9 | #e50909 10 | #FF8333 11 | #29CF40 12 | #66a3ff 13 | #0033cc 14 | #4d94ff 15 | #00c4cc 16 | #00aab1 17 | #007f85 18 | #fff 19 | #a6a6a6 20 | #c3c3c3 21 | #f2f2f2 22 | #21737373 23 | #a0737373 24 | #737373 25 | #000 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/default_colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ff0000 5 | #ffff00 6 | #00ffff 7 | #ff00ff 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4dp 5 | 8dp 6 | 10dp 7 | 16dp 8 | 32dp 9 | 14sp 10 | 16sp 11 | 36sp 12 | 56dp 13 | 48dp 14 | 20dp 15 | 30dp 16 | 10dp 17 | 10dp 18 | 35dp 19 | 20 | 21 | 10dp 22 | 4dp 23 | 10dp 24 | 10dp 25 | 4dp 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TabianCustomCamera 3 | 4 | Picture 5 | Info 6 | This sample needs camera permission. 7 | This device doesn\'t support Camera2 API. 8 | intro message 9 | 10 | 11 | 12 | Camera2 13 | View Stickers 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/test/java/codingwithmitch/com/tabiancustomcamera/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.tabiancustomcamera; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.3' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/TabianCustomCamera/b4e90e770958a787a62157720b7471d542fc01fd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 30 07:19:52 PDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------