├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── photopaint
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── photopaint
│ │ │ ├── MainActivity.java
│ │ │ ├── helpers
│ │ │ ├── AndroidUtilities.java
│ │ │ ├── ApplicationLoader.java
│ │ │ ├── Bitmaps.java
│ │ │ ├── BuildVars.java
│ │ │ ├── DispatchQueue.java
│ │ │ ├── FileLog.java
│ │ │ ├── Utilities.java
│ │ │ └── time
│ │ │ │ ├── DateParser.java
│ │ │ │ ├── DatePrinter.java
│ │ │ │ ├── FastDateFormat.java
│ │ │ │ ├── FastDateParser.java
│ │ │ │ ├── FastDatePrinter.java
│ │ │ │ ├── FormatCache.java
│ │ │ │ └── SunDate.java
│ │ │ └── views
│ │ │ ├── actionbar
│ │ │ ├── ActionBarPopupWindow.java
│ │ │ ├── AlertDialog.java
│ │ │ └── Theme.java
│ │ │ └── components
│ │ │ ├── LayoutHelper.java
│ │ │ ├── LineProgressView.java
│ │ │ ├── PhotoPaintView.java
│ │ │ ├── Point.java
│ │ │ ├── RadialProgressView.java
│ │ │ ├── Rect.java
│ │ │ ├── Size.java
│ │ │ ├── TypefaceSpan.java
│ │ │ └── paint
│ │ │ ├── Brush.java
│ │ │ ├── GLMatrix.java
│ │ │ ├── Input.java
│ │ │ ├── Painting.java
│ │ │ ├── Path.java
│ │ │ ├── PhotoFace.java
│ │ │ ├── Point.java
│ │ │ ├── Render.java
│ │ │ ├── RenderState.java
│ │ │ ├── RenderView.java
│ │ │ ├── Shader.java
│ │ │ ├── ShaderSet.java
│ │ │ ├── Slice.java
│ │ │ ├── Swatch.java
│ │ │ ├── Texture.java
│ │ │ ├── UndoStore.java
│ │ │ ├── Utils.java
│ │ │ └── views
│ │ │ ├── ColorPicker.java
│ │ │ ├── EditTextOutline.java
│ │ │ ├── EntitiesContainerView.java
│ │ │ ├── EntityView.java
│ │ │ ├── RotationGestureDetector.java
│ │ │ ├── StickerView.java
│ │ │ └── TextPaintView.java
│ └── res
│ │ ├── anim
│ │ ├── popup_in.xml
│ │ └── popup_out.xml
│ │ ├── drawable-hdpi
│ │ ├── header_shadow.png
│ │ ├── header_shadow_reverse.png
│ │ ├── ic_ab_done.png
│ │ ├── knob_shadow.png
│ │ ├── media_doc_blue.png
│ │ ├── media_doc_blue_b.png
│ │ ├── media_doc_green.png
│ │ ├── media_doc_green_b.png
│ │ ├── media_doc_load.png
│ │ ├── media_doc_pause.png
│ │ ├── media_doc_red.png
│ │ ├── media_doc_red_b.png
│ │ ├── media_doc_yellow.png
│ │ ├── media_doc_yellow_b.png
│ │ ├── photo_flip.png
│ │ ├── photo_mosaic.png
│ │ ├── photo_outline.png
│ │ ├── photo_paint.png
│ │ ├── photo_paint_brush.png
│ │ ├── photo_paint_text.png
│ │ ├── photo_recover.png
│ │ ├── photo_sticker.png
│ │ ├── photo_undo.png
│ │ ├── popup_fixed_alert.9.png
│ │ ├── popup_fixed_alert2.9.png
│ │ └── popup_fixed_top.9.png
│ │ ├── drawable-mdpi
│ │ ├── header_shadow.png
│ │ ├── header_shadow_reverse.png
│ │ ├── ic_ab_done.png
│ │ ├── knob_shadow.png
│ │ ├── media_doc_blue.png
│ │ ├── media_doc_blue_b.png
│ │ ├── media_doc_green.png
│ │ ├── media_doc_green_b.png
│ │ ├── media_doc_load.png
│ │ ├── media_doc_pause.png
│ │ ├── media_doc_red.png
│ │ ├── media_doc_red_b.png
│ │ ├── media_doc_yellow.png
│ │ ├── media_doc_yellow_b.png
│ │ ├── photo_flip.png
│ │ ├── photo_mosaic.png
│ │ ├── photo_outline.png
│ │ ├── photo_paint.png
│ │ ├── photo_paint_brush.png
│ │ ├── photo_paint_text.png
│ │ ├── photo_recover.png
│ │ ├── photo_sticker.png
│ │ ├── photo_undo.png
│ │ ├── popup_fixed_alert.9.png
│ │ ├── popup_fixed_alert2.9.png
│ │ └── popup_fixed_top.9.png
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xhdpi
│ │ ├── header_shadow.png
│ │ ├── header_shadow_reverse.png
│ │ ├── ic_ab_done.png
│ │ ├── knob_shadow.png
│ │ ├── media_doc_blue.png
│ │ ├── media_doc_blue_b.png
│ │ ├── media_doc_green.png
│ │ ├── media_doc_green_b.png
│ │ ├── media_doc_load.png
│ │ ├── media_doc_pause.png
│ │ ├── media_doc_red.png
│ │ ├── media_doc_red_b.png
│ │ ├── media_doc_yellow.png
│ │ ├── media_doc_yellow_b.png
│ │ ├── photo_flip.png
│ │ ├── photo_mosaic.png
│ │ ├── photo_outline.png
│ │ ├── photo_paint.png
│ │ ├── photo_paint_brush.png
│ │ ├── photo_paint_text.png
│ │ ├── photo_recover.png
│ │ ├── photo_sticker.png
│ │ ├── photo_undo.png
│ │ ├── popup_fixed_alert.9.png
│ │ ├── popup_fixed_alert2.9.png
│ │ └── popup_fixed_top.9.png
│ │ ├── drawable-xxhdpi
│ │ ├── adv_img_2.png
│ │ ├── header_shadow.png
│ │ ├── header_shadow_reverse.png
│ │ ├── ic_ab_done.png
│ │ ├── knob_shadow.png
│ │ ├── media_doc_blue.png
│ │ ├── media_doc_blue_b.png
│ │ ├── media_doc_green.png
│ │ ├── media_doc_green_b.png
│ │ ├── media_doc_load.png
│ │ ├── media_doc_pause.png
│ │ ├── media_doc_red.png
│ │ ├── media_doc_red_b.png
│ │ ├── media_doc_yellow.png
│ │ ├── media_doc_yellow_b.png
│ │ ├── photo_flip.png
│ │ ├── photo_mosaic.png
│ │ ├── photo_outline.png
│ │ ├── photo_paint.png
│ │ ├── photo_paint_brush.png
│ │ ├── photo_paint_text.png
│ │ ├── photo_recover.png
│ │ ├── photo_sticker.png
│ │ ├── photo_undo.png
│ │ ├── popup_fixed_alert.9.png
│ │ ├── popup_fixed_alert2.9.png
│ │ └── popup_fixed_top.9.png
│ │ ├── drawable
│ │ ├── adv_img_2.png
│ │ ├── catstile.jpg
│ │ ├── field_carret_empty.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── mosaic_black.png
│ │ ├── mosaic_white.png
│ │ ├── paint_elliptical_brush.png
│ │ ├── paint_elliptical_preview.png
│ │ ├── paint_neon_brush.png
│ │ ├── paint_neon_preview.png
│ │ ├── paint_radial_brush.png
│ │ ├── paint_radial_preview.png
│ │ ├── screenshot.png
│ │ ├── sticker_demo.png
│ │ └── transparent.xml
│ │ ├── layout
│ │ └── activity_main.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
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── photopaint
│ └── ExampleUnitTest.kt
├── build.gradle
├── gifs
├── 1571733722289.gif
├── 1571733767440.gif
├── 1571733806801.gif
├── 1571733849769.gif
└── 1571733896635.gif
├── 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/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PhotoPaint
2 | 1.基于OpenGL技术的图片编辑功能模块(包含画笔涂鸦,贴纸,贴文字和画刷风格的马赛克),交互体验也很丰富。
3 | 
4 | 
5 | 
6 | 
7 | 
8 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion 28
9 | defaultConfig {
10 | applicationId "com.example.photopaint"
11 | minSdkVersion 15
12 | targetSdkVersion 28
13 | versionCode 1
14 | versionName "1.0"
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | configurations {
26 | compile.exclude module: 'support-v4'
27 | }
28 |
29 | dependencies {
30 | implementation 'androidx.core:core:1.1.0-beta01'
31 | implementation 'androidx.palette:palette:1.0.0'
32 | implementation 'androidx.exifinterface:exifinterface:1.0.0'
33 |
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
36 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
37 | implementation 'com.google.android.gms:play-services-maps:16.0.0'
38 | implementation 'com.google.android.gms:play-services-auth:16.0.1'
39 | implementation 'com.google.android.gms:play-services-vision:16.2.0'
40 | implementation 'com.google.android.gms:play-services-wallet:16.0.1'
41 | implementation 'com.google.android.gms:play-services-wearable:16.0.1'
42 | }
43 |
--------------------------------------------------------------------------------
/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/com/example/photopaint/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.photopaint
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("com.example.photopaint", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint;
2 |
3 | import android.app.Activity;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Color;
6 | import android.os.Bundle;
7 | import android.view.Gravity;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.FrameLayout;
11 | import com.example.photopaint.views.components.LayoutHelper;
12 | import com.example.photopaint.views.components.PhotoPaintView;
13 |
14 | public class MainActivity extends Activity {
15 | private FrameLayout frameLayout;
16 | private PhotoPaintView photoPaintView;
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 |
22 | frameLayout = new FrameLayout(this);
23 | frameLayout.setBackgroundColor(Color.GRAY);
24 | setContentView(frameLayout);
25 |
26 | photoPaintView = new PhotoPaintView(MainActivity.this, BitmapFactory.decodeResource(getResources(), R.drawable.adv_img_2), 0);
27 | frameLayout.addView(photoPaintView, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
28 | photoPaintView.init();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/ApplicationLoader.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Application;
5 | import android.content.Context;
6 | import android.os.Handler;
7 |
8 | public class ApplicationLoader extends Application {
9 |
10 | @SuppressLint("StaticFieldLeak")
11 | public static volatile Context applicationContext;
12 | public static volatile Handler applicationHandler;
13 |
14 | public ApplicationLoader() {
15 | super();
16 | }
17 |
18 | @Override
19 | public void onCreate() {
20 | try {
21 | applicationContext = getApplicationContext();
22 | } catch (Throwable ignore) {
23 |
24 | }
25 | super.onCreate();
26 |
27 | if (applicationContext == null) {
28 | applicationContext = getApplicationContext();
29 | }
30 |
31 | applicationHandler = new Handler(applicationContext.getMainLooper());
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/BuildVars.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | public class BuildVars {
7 |
8 | public static boolean DEBUG_VERSION = false;
9 | public static boolean DEBUG_PRIVATE_VERSION = false;
10 | public static boolean LOGS_ENABLED = false;
11 | public static boolean USE_CLOUD_STRINGS = true;
12 | public static boolean CHECK_UPDATES = false;
13 | public static int BUILD_VERSION = 1608;
14 | public static String BUILD_VERSION_STRING = "5.7.0";
15 | public static int APP_ID = 800906; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id
16 | public static String APP_HASH = "ff0354a8224b666e3486875143ce0278"; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id
17 | public static String HOCKEY_APP_HASH = "00000000000000000000000000000000";
18 | public static String HOCKEY_APP_HASH_DEBUG = "00000000000000000000000000000000";
19 | public static String SMS_HASH = ""; //https://developers.google.com/identity/sms-retriever/overview
20 | public static String PLAYSTORE_APP_URL = "";
21 |
22 | static {
23 | if (ApplicationLoader.applicationContext != null) {
24 | SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("systemConfig", Context.MODE_PRIVATE);
25 | LOGS_ENABLED = sharedPreferences.getBoolean("logsEnabled", DEBUG_VERSION);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/DispatchQueue.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 | import android.os.Message;
6 |
7 | import java.util.concurrent.CountDownLatch;
8 |
9 | public class DispatchQueue extends Thread {
10 |
11 | private volatile Handler handler = null;
12 | private CountDownLatch syncLatch = new CountDownLatch(1);
13 |
14 | public DispatchQueue(final String threadName) {
15 | setName(threadName);
16 | start();
17 | }
18 |
19 | public void sendMessage(Message msg, int delay) {
20 | try {
21 | syncLatch.await();
22 | if (delay <= 0) {
23 | handler.sendMessage(msg);
24 | } else {
25 | handler.sendMessageDelayed(msg, delay);
26 | }
27 | } catch (Exception e) {
28 | FileLog.e(e);
29 | }
30 | }
31 |
32 | public void cancelRunnable(Runnable runnable) {
33 | try {
34 | syncLatch.await();
35 | handler.removeCallbacks(runnable);
36 | } catch (Exception e) {
37 | FileLog.e(e);
38 | }
39 | }
40 |
41 | public void postRunnable(Runnable runnable) {
42 | postRunnable(runnable, 0);
43 | }
44 |
45 | public void postRunnable(Runnable runnable, long delay) {
46 | try {
47 | syncLatch.await();
48 | } catch (Exception e) {
49 | FileLog.e(e);
50 | }
51 | if (delay <= 0) {
52 | handler.post(runnable);
53 | } else {
54 | handler.postDelayed(runnable, delay);
55 | }
56 | }
57 |
58 | public void cleanupQueue() {
59 | try {
60 | syncLatch.await();
61 | handler.removeCallbacksAndMessages(null);
62 | } catch (Exception e) {
63 | FileLog.e(e);
64 | }
65 | }
66 |
67 | public void handleMessage(Message inputMessage) {
68 |
69 | }
70 |
71 | public void recycle() {
72 | handler.getLooper().quit();
73 | }
74 |
75 | @Override
76 | public void run() {
77 | Looper.prepare();
78 | handler = new Handler() {
79 | @Override
80 | public void handleMessage(Message msg) {
81 | DispatchQueue.this.handleMessage(msg);
82 | }
83 | };
84 | syncLatch.countDown();
85 | Looper.loop();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/FileLog.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers;
2 |
3 | import android.util.Log;
4 | import com.example.photopaint.helpers.time.FastDateFormat;
5 |
6 | import java.io.File;
7 | import java.io.FileOutputStream;
8 | import java.io.OutputStreamWriter;
9 | import java.util.Locale;
10 |
11 | public class FileLog {
12 | private OutputStreamWriter streamWriter = null;
13 | private FastDateFormat dateFormat = null;
14 | private DispatchQueue logQueue = null;
15 | private File currentFile = null;
16 | private File networkFile = null;
17 | private boolean initied;
18 |
19 | private final static String tag = "tmessages";
20 |
21 | private static volatile FileLog Instance = null;
22 |
23 | public static FileLog getInstance() {
24 | FileLog localInstance = Instance;
25 | if (localInstance == null) {
26 | synchronized (FileLog.class) {
27 | localInstance = Instance;
28 | if (localInstance == null) {
29 | Instance = localInstance = new FileLog();
30 | }
31 | }
32 | }
33 | return localInstance;
34 | }
35 |
36 | public FileLog() {
37 | if (!BuildVars.LOGS_ENABLED) {
38 | return;
39 | }
40 | init();
41 | }
42 |
43 | public void init() {
44 | if (initied) {
45 | return;
46 | }
47 | dateFormat = FastDateFormat.getInstance("dd_MM_yyyy_HH_mm_ss", Locale.US);
48 | try {
49 | File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
50 | if (sdCard == null) {
51 | return;
52 | }
53 | File dir = new File(sdCard.getAbsolutePath() + "/logs");
54 | dir.mkdirs();
55 | currentFile = new File(dir, dateFormat.format(System.currentTimeMillis()) + ".txt");
56 | } catch (Exception e) {
57 | e.printStackTrace();
58 | }
59 | try {
60 | logQueue = new DispatchQueue("logQueue");
61 | currentFile.createNewFile();
62 | FileOutputStream stream = new FileOutputStream(currentFile);
63 | streamWriter = new OutputStreamWriter(stream);
64 | streamWriter.write("-----start log " + dateFormat.format(System.currentTimeMillis()) + "-----\n");
65 | streamWriter.flush();
66 | } catch (Exception e) {
67 | e.printStackTrace();
68 | }
69 | initied = true;
70 | }
71 |
72 | public static void ensureInitied() {
73 | getInstance().init();
74 | }
75 |
76 | public static String getNetworkLogPath() {
77 | if (!BuildVars.LOGS_ENABLED) {
78 | return "";
79 | }
80 | try {
81 | File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
82 | if (sdCard == null) {
83 | return "";
84 | }
85 | File dir = new File(sdCard.getAbsolutePath() + "/logs");
86 | dir.mkdirs();
87 | getInstance().networkFile = new File(dir, getInstance().dateFormat.format(System.currentTimeMillis()) + "_net.txt");
88 | return getInstance().networkFile.getAbsolutePath();
89 | } catch (Throwable e) {
90 | e.printStackTrace();
91 | }
92 | return "";
93 | }
94 |
95 | public static void e(final String message, final Throwable exception) {
96 | if (!BuildVars.LOGS_ENABLED) {
97 | return;
98 | }
99 | ensureInitied();
100 | Log.e(tag, message, exception);
101 | if (getInstance().streamWriter != null) {
102 | getInstance().logQueue.postRunnable(new Runnable() {
103 | @Override
104 | public void run() {
105 | try {
106 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + message + "\n");
107 | getInstance().streamWriter.write(exception.toString());
108 | getInstance().streamWriter.flush();
109 | } catch (Exception e) {
110 | e.printStackTrace();
111 | }
112 | }
113 | });
114 | }
115 | }
116 |
117 | public static void e(final String message) {
118 | if (!BuildVars.LOGS_ENABLED) {
119 | return;
120 | }
121 | ensureInitied();
122 | Log.e(tag, message);
123 | if (getInstance().streamWriter != null) {
124 | getInstance().logQueue.postRunnable(new Runnable() {
125 | @Override
126 | public void run() {
127 | try {
128 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + message + "\n");
129 | getInstance().streamWriter.flush();
130 | } catch (Exception e) {
131 | e.printStackTrace();
132 | }
133 | }
134 | });
135 | }
136 | }
137 |
138 | public static void e(final Throwable e) {
139 | if (!BuildVars.LOGS_ENABLED) {
140 | return;
141 | }
142 | ensureInitied();
143 | e.printStackTrace();
144 | if (getInstance().streamWriter != null) {
145 |
146 | getInstance().logQueue.postRunnable(new Runnable() {
147 | @Override
148 | public void run() {
149 | try {
150 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + e + "\n");
151 | StackTraceElement[] stack = e.getStackTrace();
152 | for (int a = 0; a < stack.length; a++) {
153 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/tmessages: " + stack[a] + "\n");
154 | }
155 | getInstance().streamWriter.flush();
156 | } catch (Exception e1) {
157 | e1.printStackTrace();
158 | }
159 | }
160 | });
161 | } else {
162 | e.printStackTrace();
163 | }
164 | }
165 |
166 | public static void d(final String message) {
167 | if (!BuildVars.LOGS_ENABLED) {
168 | return;
169 | }
170 | ensureInitied();
171 | Log.d(tag, message);
172 | if (getInstance().streamWriter != null) {
173 | getInstance().logQueue.postRunnable(new Runnable() {
174 | @Override
175 | public void run() {
176 | try {
177 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " D/tmessages: " + message + "\n");
178 | getInstance().streamWriter.flush();
179 | } catch (Exception e) {
180 | e.printStackTrace();
181 | }
182 | }
183 | });
184 | }
185 | }
186 |
187 | public static void w(final String message) {
188 | if (!BuildVars.LOGS_ENABLED) {
189 | return;
190 | }
191 | ensureInitied();
192 | Log.w(tag, message);
193 | if (getInstance().streamWriter != null) {
194 | getInstance().logQueue.postRunnable(new Runnable() {
195 | @Override
196 | public void run() {
197 | try {
198 | getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " W/tmessages: " + message + "\n");
199 | getInstance().streamWriter.flush();
200 | } catch (Exception e) {
201 | e.printStackTrace();
202 | }
203 | }
204 | });
205 | }
206 | }
207 |
208 | public static void cleanupLogs() {
209 | ensureInitied();
210 | File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
211 | if (sdCard == null) {
212 | return;
213 | }
214 | File dir = new File(sdCard.getAbsolutePath() + "/logs");
215 | File[] files = dir.listFiles();
216 | if (files != null) {
217 | for (int a = 0; a < files.length; a++) {
218 | File file = files[a];
219 | if (getInstance().currentFile != null && file.getAbsolutePath().equals(getInstance().currentFile.getAbsolutePath())) {
220 | continue;
221 | }
222 | if (getInstance().networkFile != null && file.getAbsolutePath().equals(getInstance().networkFile.getAbsolutePath())) {
223 | continue;
224 | }
225 | file.delete();
226 | }
227 | }
228 | }
229 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/time/DateParser.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers.time;
2 |
3 | import java.text.ParseException;
4 | import java.text.ParsePosition;
5 | import java.util.Date;
6 | import java.util.Locale;
7 | import java.util.TimeZone;
8 |
9 | public interface DateParser {
10 |
11 | /**
12 | * Equivalent to DateFormat.parse(String).
13 | *
14 | * See {@link java.text.DateFormat#parse(String)} for more information.
15 | *
16 | * @param source A String
whose beginning should be parsed.
17 | * @return A Date
parsed from the string
18 | * @throws ParseException if the beginning of the specified string cannot be parsed.
19 | */
20 | Date parse(String source) throws ParseException;
21 |
22 | /**
23 | * Equivalent to DateFormat.parse(String, ParsePosition).
24 | *
25 | * See {@link java.text.DateFormat#parse(String, ParsePosition)} for more information.
26 | *
27 | * @param source A String
, part of which should be parsed.
28 | * @param pos A ParsePosition
object with index and error index information
29 | * as described above.
30 | * @return A Date
parsed from the string. In case of error, returns null.
31 | * @throws NullPointerException if text or pos is null.
32 | */
33 | Date parse(String source, ParsePosition pos);
34 |
35 | // Accessors
36 | //-----------------------------------------------------------------------
37 |
38 | /**
39 | * Get the pattern used by this parser.
40 | *
41 | * @return the pattern, {@link java.text.SimpleDateFormat} compatible
42 | */
43 | String getPattern();
44 |
45 | /**
46 | *
47 | * Get the time zone used by this parser.
48 | *
49 | *
50 | *
51 | * The default {@link TimeZone} used to create a {@link Date} when the {@link TimeZone} is not specified by
52 | * the format pattern.
53 | *
54 | *
55 | * @return the time zone
56 | */
57 | TimeZone getTimeZone();
58 |
59 | /**
60 | * Get the locale used by this parser.
61 | *
62 | * @return the locale
63 | */
64 | Locale getLocale();
65 |
66 | /**
67 | * Parses text from a string to produce a Date.
68 | *
69 | * @param source A String
whose beginning should be parsed.
70 | * @return a java.util.Date
object
71 | * @throws ParseException if the beginning of the specified string cannot be parsed.
72 | * @see java.text.DateFormat#parseObject(String)
73 | */
74 | Object parseObject(String source) throws ParseException;
75 |
76 | /**
77 | * Parse a date/time string according to the given parse position.
78 | *
79 | * @param source A String
whose beginning should be parsed.
80 | * @param pos the parse position
81 | * @return a java.util.Date
object
82 | * @see java.text.DateFormat#parseObject(String, ParsePosition)
83 | */
84 | Object parseObject(String source, ParsePosition pos);
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/time/DatePrinter.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers.time;
2 |
3 | import java.text.FieldPosition;
4 | import java.util.Calendar;
5 | import java.util.Date;
6 | import java.util.Locale;
7 | import java.util.TimeZone;
8 |
9 | public interface DatePrinter {
10 |
11 | /**
12 | * Formats a millisecond {@code long} value.
13 | *
14 | * @param millis the millisecond value to format
15 | * @return the formatted string
16 | * @since 2.1
17 | */
18 | String format(long millis);
19 |
20 | /**
21 | * Formats a {@code Date} object using a {@code GregorianCalendar}.
22 | *
23 | * @param date the date to format
24 | * @return the formatted string
25 | */
26 | String format(Date date);
27 |
28 | /**
29 | * Formats a {@code Calendar} object.
30 | *
31 | * @param calendar the calendar to format
32 | * @return the formatted string
33 | */
34 | String format(Calendar calendar);
35 |
36 | /**
37 | * Formats a milliseond {@code long} value into the
38 | * supplied {@code StringBuffer}.
39 | *
40 | * @param millis the millisecond value to format
41 | * @param buf the buffer to format into
42 | * @return the specified string buffer
43 | */
44 | StringBuffer format(long millis, StringBuffer buf);
45 |
46 | /**
47 | * Formats a {@code Date} object into the
48 | * supplied {@code StringBuffer} using a {@code GregorianCalendar}.
49 | *
50 | * @param date the date to format
51 | * @param buf the buffer to format into
52 | * @return the specified string buffer
53 | */
54 | StringBuffer format(Date date, StringBuffer buf);
55 |
56 | /**
57 | * Formats a {@code Calendar} object into the
58 | * supplied {@code StringBuffer}.
59 | *
60 | * @param calendar the calendar to format
61 | * @param buf the buffer to format into
62 | * @return the specified string buffer
63 | */
64 | StringBuffer format(Calendar calendar, StringBuffer buf);
65 |
66 | // Accessors
67 | //-----------------------------------------------------------------------
68 |
69 | /**
70 | * Gets the pattern used by this printer.
71 | *
72 | * @return the pattern, {@link java.text.SimpleDateFormat} compatible
73 | */
74 | String getPattern();
75 |
76 | /**
77 | * Gets the time zone used by this printer.
78 | *
79 | * This zone is always used for {@code Date} printing.
80 | *
81 | * @return the time zone
82 | */
83 | TimeZone getTimeZone();
84 |
85 | /**
86 | * Gets the locale used by this printer.
87 | *
88 | * @return the locale
89 | */
90 | Locale getLocale();
91 |
92 | /**
93 | * Formats a {@code Date}, {@code Calendar} or
94 | * {@code Long} (milliseconds) object.
95 | *
96 | * See {@link java.text.DateFormat#format(Object, StringBuffer, FieldPosition)}
97 | *
98 | * @param obj the object to format
99 | * @param toAppendTo the buffer to append to
100 | * @param pos the position - ignored
101 | * @return the buffer passed in
102 | */
103 | StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos);
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/time/FormatCache.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers.time;
2 |
3 | import java.text.DateFormat;
4 | import java.text.Format;
5 | import java.text.SimpleDateFormat;
6 | import java.util.Arrays;
7 | import java.util.Locale;
8 | import java.util.TimeZone;
9 | import java.util.concurrent.ConcurrentHashMap;
10 | import java.util.concurrent.ConcurrentMap;
11 |
12 |
13 | abstract class FormatCache {
14 | /**
15 | * No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
16 | */
17 | static final int NONE = -1;
18 |
19 | private final ConcurrentMap cInstanceCache
20 | = new ConcurrentHashMap(7);
21 |
22 | private static final ConcurrentMap cDateTimeInstanceCache
23 | = new ConcurrentHashMap(7);
24 |
25 | /**
26 | * Gets a formatter instance using the default pattern in the
27 | * default timezone and locale.
28 | *
29 | * @return a date/time formatter
30 | */
31 | public F getInstance() {
32 | return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
33 | }
34 |
35 | /**
36 | * Gets a formatter instance using the specified pattern, time zone
37 | * and locale.
38 | *
39 | * @param pattern {@link SimpleDateFormat} compatible
40 | * pattern, non-null
41 | * @param timeZone the time zone, null means use the default TimeZone
42 | * @param locale the locale, null means use the default Locale
43 | * @return a pattern based date/time formatter
44 | * @throws IllegalArgumentException if pattern is invalid
45 | * or null
46 | */
47 | public F getInstance(final String pattern, TimeZone timeZone, Locale locale) {
48 | if (pattern == null) {
49 | throw new NullPointerException("pattern must not be null");
50 | }
51 | if (timeZone == null) {
52 | timeZone = TimeZone.getDefault();
53 | }
54 | if (locale == null) {
55 | locale = Locale.getDefault();
56 | }
57 | final MultipartKey key = new MultipartKey(pattern, timeZone, locale);
58 | F format = cInstanceCache.get(key);
59 | if (format == null) {
60 | format = createInstance(pattern, timeZone, locale);
61 | final F previousValue = cInstanceCache.putIfAbsent(key, format);
62 | if (previousValue != null) {
63 | // another thread snuck in and did the same work
64 | // we should return the instance that is in ConcurrentMap
65 | format = previousValue;
66 | }
67 | }
68 | return format;
69 | }
70 |
71 | /**
72 | * Create a format instance using the specified pattern, time zone
73 | * and locale.
74 | *
75 | * @param pattern {@link SimpleDateFormat} compatible pattern, this will not be null.
76 | * @param timeZone time zone, this will not be null.
77 | * @param locale locale, this will not be null.
78 | * @return a pattern based date/time formatter
79 | * @throws IllegalArgumentException if pattern is invalid
80 | * or null
81 | */
82 | abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
83 |
84 | /**
85 | * Gets a date/time formatter instance using the specified style,
86 | * time zone and locale.
87 | *
88 | * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
89 | * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
90 | * @param timeZone optional time zone, overrides time zone of
91 | * formatted date, null means use default Locale
92 | * @param locale optional locale, overrides system locale
93 | * @return a localized standard date/time formatter
94 | * @throws IllegalArgumentException if the Locale has no date/time
95 | * pattern defined
96 | */
97 | // This must remain private, see LANG-884
98 | private F getDateTimeInstance(final Integer dateStyle, final Integer timeStyle, final TimeZone timeZone, Locale locale) {
99 | if (locale == null) {
100 | locale = Locale.getDefault();
101 | }
102 | final String pattern = getPatternForStyle(dateStyle, timeStyle, locale);
103 | return getInstance(pattern, timeZone, locale);
104 | }
105 |
106 | /**
107 | * Gets a date/time formatter instance using the specified style,
108 | * time zone and locale.
109 | *
110 | * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
111 | * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
112 | * @param timeZone optional time zone, overrides time zone of
113 | * formatted date, null means use default Locale
114 | * @param locale optional locale, overrides system locale
115 | * @return a localized standard date/time formatter
116 | * @throws IllegalArgumentException if the Locale has no date/time
117 | * pattern defined
118 | */
119 | // package protected, for access from FastDateFormat; do not make public or protected
120 | F getDateTimeInstance(final int dateStyle, final int timeStyle, final TimeZone timeZone, Locale locale) {
121 | return getDateTimeInstance(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle), timeZone, locale);
122 | }
123 |
124 | /**
125 | * Gets a date formatter instance using the specified style,
126 | * time zone and locale.
127 | *
128 | * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
129 | * @param timeZone optional time zone, overrides time zone of
130 | * formatted date, null means use default Locale
131 | * @param locale optional locale, overrides system locale
132 | * @return a localized standard date/time formatter
133 | * @throws IllegalArgumentException if the Locale has no date/time
134 | * pattern defined
135 | */
136 | // package protected, for access from FastDateFormat; do not make public or protected
137 | F getDateInstance(final int dateStyle, final TimeZone timeZone, Locale locale) {
138 | return getDateTimeInstance(Integer.valueOf(dateStyle), null, timeZone, locale);
139 | }
140 |
141 | /**
142 | * Gets a time formatter instance using the specified style,
143 | * time zone and locale.
144 | *
145 | * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
146 | * @param timeZone optional time zone, overrides time zone of
147 | * formatted date, null means use default Locale
148 | * @param locale optional locale, overrides system locale
149 | * @return a localized standard date/time formatter
150 | * @throws IllegalArgumentException if the Locale has no date/time
151 | * pattern defined
152 | */
153 | // package protected, for access from FastDateFormat; do not make public or protected
154 | F getTimeInstance(final int timeStyle, final TimeZone timeZone, Locale locale) {
155 | return getDateTimeInstance(null, Integer.valueOf(timeStyle), timeZone, locale);
156 | }
157 |
158 | /**
159 | * Gets a date/time format for the specified styles and locale.
160 | *
161 | * @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT, null indicates no date in format
162 | * @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT, null indicates no time in format
163 | * @param locale The non-null locale of the desired format
164 | * @return a localized standard date/time format
165 | * @throws IllegalArgumentException if the Locale has no date/time pattern defined
166 | */
167 | // package protected, for access from test code; do not make public or protected
168 | static String getPatternForStyle(final Integer dateStyle, final Integer timeStyle, final Locale locale) {
169 | final MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
170 |
171 | String pattern = cDateTimeInstanceCache.get(key);
172 | if (pattern == null) {
173 | try {
174 | DateFormat formatter;
175 | if (dateStyle == null) {
176 | formatter = DateFormat.getTimeInstance(timeStyle.intValue(), locale);
177 | } else if (timeStyle == null) {
178 | formatter = DateFormat.getDateInstance(dateStyle.intValue(), locale);
179 | } else {
180 | formatter = DateFormat.getDateTimeInstance(dateStyle.intValue(), timeStyle.intValue(), locale);
181 | }
182 | pattern = ((SimpleDateFormat) formatter).toPattern();
183 | final String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
184 | if (previous != null) {
185 | // even though it doesn't matter if another thread put the pattern
186 | // it's still good practice to return the String instance that is
187 | // actually in the ConcurrentMap
188 | pattern = previous;
189 | }
190 | } catch (final ClassCastException ex) {
191 | throw new IllegalArgumentException("No date time pattern for locale: " + locale);
192 | }
193 | }
194 | return pattern;
195 | }
196 |
197 | // ----------------------------------------------------------------------
198 |
199 | /**
200 | * Helper class to hold multi-part Map keys
201 | */
202 | private static class MultipartKey {
203 | private final Object[] keys;
204 | private int hashCode;
205 |
206 | /**
207 | * Constructs an instance of MultipartKey
to hold the specified objects.
208 | *
209 | * @param keys the set of objects that make up the key. Each key may be null.
210 | */
211 | public MultipartKey(final Object... keys) {
212 | this.keys = keys;
213 | }
214 |
215 | /**
216 | * {@inheritDoc}
217 | */
218 | @Override
219 | public boolean equals(final Object obj) {
220 | // Eliminate the usual boilerplate because
221 | // this inner static class is only used in a generic ConcurrentHashMap
222 | // which will not compare against other Object types
223 | return Arrays.equals(keys, ((MultipartKey) obj).keys);
224 | }
225 |
226 | /**
227 | * {@inheritDoc}
228 | */
229 | @Override
230 | public int hashCode() {
231 | if (hashCode == 0) {
232 | int rc = 0;
233 | for (final Object key : keys) {
234 | if (key != null) {
235 | rc = rc * 7 + key.hashCode();
236 | }
237 | }
238 | hashCode = rc;
239 | }
240 | return hashCode;
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/helpers/time/SunDate.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.helpers.time;
2 |
3 | import java.util.Calendar;
4 | import java.util.TimeZone;
5 |
6 | public class SunDate {
7 |
8 | private static final double DEGRAD = Math.PI / 180.0;
9 | private static final double RADEG = 180.0 / Math.PI;
10 | private static final double INV360 = 1.0 / 360.0;
11 |
12 | private static long days_since_2000_Jan_0(int y, int m, int d) {
13 | return 367L * y - ((7 * (y + ((m + 9) / 12))) / 4) + ((275 * m) / 9) + d - 730530L;
14 | }
15 |
16 | private static double revolution(double x) {
17 | return x - 360.0 * Math.floor(x * INV360);
18 | }
19 |
20 | private static double rev180(double x) {
21 | return x - 360.0 * Math.floor(x * INV360 + 0.5);
22 | }
23 |
24 | private static double GMST0(double d) {
25 | return revolution((180.0 + 356.0470 + 282.9404) + (0.9856002585 + 4.70935E-5) * d);
26 | }
27 |
28 | private static double sind(double x) {
29 | return Math.sin(x * DEGRAD);
30 | }
31 |
32 | private static double cosd(double x) {
33 | return Math.cos(x * DEGRAD);
34 | }
35 |
36 | private static double tand(double x) {
37 | return Math.tan(x * DEGRAD);
38 | }
39 |
40 | private static double acosd(double x) {
41 | return RADEG * Math.acos(x);
42 | }
43 |
44 | private static double atan2d(double y, double x) {
45 | return RADEG * Math.atan2(y, x);
46 | }
47 |
48 | private static void sunposAtDay(double p, double[] ot, double[] d) {
49 | double S, a, V, l, k, i;
50 |
51 | S = revolution(356.0470 + 0.9856002585 * p);
52 | l = 282.9404 + 4.70935E-5 * p;
53 | a = 0.016709 - 1.151E-9 * p;
54 |
55 | V = a * RADEG * sind(S) * (1.0 + a * cosd(S)) + S;
56 | k = cosd(V) - a;
57 |
58 | i = Math.sqrt(1.0 - a * a) * sind(V);
59 | d[0] = Math.sqrt(k * k + i * i);
60 | i = atan2d(i, k);
61 | ot[0] = i + l;
62 | if (ot[0] >= 360.0) {
63 | ot[0] -= 360.0;
64 | }
65 | }
66 |
67 | private static void sun_RA_decAtDay(double d, double[] RA, double[] dec, double[] r) {
68 | double[] lon = new double[1];
69 | double obl_ecl;
70 | double xs, ys;
71 | double xe, ye, ze;
72 |
73 | sunposAtDay(d, lon, r);
74 |
75 | xs = r[0] * cosd(lon[0]);
76 | ys = r[0] * sind(lon[0]);
77 |
78 | obl_ecl = 23.4393 - 3.563E-7 * d;
79 |
80 | xe = xs;
81 | ye = ys * cosd(obl_ecl);
82 | ze = ys * sind(obl_ecl);
83 |
84 | RA[0] = atan2d(ye, xe);
85 | dec[0] = atan2d(ze, Math.sqrt(xe * xe + ye * ye));
86 | }
87 |
88 | private static int sunRiseSetHelperForYear(int year, int month, int day, double lon, double lat, double altit, int upper_limb, double[] sun) {
89 | double[] sRA = new double[1];
90 | double[] sdec = new double[1];
91 | double[] sr = new double[1];
92 |
93 | double d, sradius, t, tsouth, sidtime;
94 | int rc = 0;
95 | d = days_since_2000_Jan_0(year, month, day) + 0.5 - lon / 360.0;
96 | sidtime = revolution(GMST0(d) + 180.0 + lon);
97 | sun_RA_decAtDay(d, sRA, sdec, sr);
98 | tsouth = 12.0 - rev180(sidtime - sRA[0]) / 15.0;
99 | sradius = 0.2666 / sr[0];
100 | if (upper_limb != 0) {
101 | altit -= sradius;
102 | }
103 |
104 | double cost;
105 | cost = (sind(altit) - sind(lat) * sind(sdec[0])) / (cosd(lat) * cosd(sdec[0]));
106 | if (cost >= 1.0) {
107 | rc = -1;
108 | t = 0.0;
109 | } else if (cost <= -1.0) {
110 | rc = +1;
111 | t = 12.0;
112 | } else {
113 | t = acosd(cost) / 15.0;
114 | }
115 | sun[0] = tsouth - t;
116 | sun[1] = tsouth + t;
117 |
118 | return rc;
119 | }
120 |
121 | private static int sunRiseSetForYear(int year, int month, int day, double lon, double lat, double[] sun) {
122 | return sunRiseSetHelperForYear(year, month, day, lon, lat, (-35.0 / 60.0), 1, sun);
123 | }
124 |
125 | public static int[] calculateSunriseSunset(double lat, double lon) {
126 | Calendar calendar = Calendar.getInstance();
127 | calendar.setTimeInMillis(System.currentTimeMillis());
128 | double[] sun = new double[2];
129 | sunRiseSetForYear(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH), lon, lat, sun);
130 | int timeZoneOffset = TimeZone.getDefault().getOffset(System.currentTimeMillis()) / 1000 / 60;
131 | int sunrise = (int) (sun[0] * 60) + timeZoneOffset;
132 | int sunset = (int) (sun[1] * 60) + timeZoneOffset;
133 | if (sunrise < 0) {
134 | sunrise += 60 * 24;
135 | } else if (sunrise > 60 * 24) {
136 | sunrise -= 60 * 24;
137 | }
138 | if (sunset < 0) {
139 | sunset += 60 * 24;
140 | } else if (sunset > 60 * 24) {
141 | sunset += 60 * 24;
142 | }
143 | return new int[] {sunrise, sunset};
144 | }
145 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/LayoutHelper.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components;
2 |
3 | import android.widget.FrameLayout;
4 | import android.widget.LinearLayout;
5 | import android.widget.RelativeLayout;
6 | import android.widget.ScrollView;
7 |
8 | import com.example.photopaint.helpers.AndroidUtilities;
9 |
10 | public class LayoutHelper {
11 |
12 | public static final int MATCH_PARENT = -1;
13 | public static final int WRAP_CONTENT = -2;
14 |
15 | private static int getSize(float size) {
16 | return (int) (size < 0 ? size : AndroidUtilities.dp(size));
17 | }
18 |
19 | public static ScrollView.LayoutParams createScroll(int width, int height, int gravity) {
20 | return new ScrollView.LayoutParams(getSize(width), getSize(height), gravity);
21 | }
22 |
23 | public static ScrollView.LayoutParams createScroll(int width, int height, int gravity, float leftMargin, float topMargin, float rightMargin, float bottomMargin) {
24 | ScrollView.LayoutParams layoutParams = new ScrollView.LayoutParams(getSize(width), getSize(height), gravity);
25 | layoutParams.leftMargin = AndroidUtilities.dp(leftMargin);
26 | layoutParams.topMargin = AndroidUtilities.dp(topMargin);
27 | layoutParams.rightMargin = AndroidUtilities.dp(rightMargin);
28 | layoutParams.bottomMargin = AndroidUtilities.dp(bottomMargin);
29 | return layoutParams;
30 | }
31 |
32 | public static FrameLayout.LayoutParams createFrame(int width, float height, int gravity, float leftMargin, float topMargin, float rightMargin, float bottomMargin) {
33 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity);
34 | layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin));
35 | return layoutParams;
36 | }
37 |
38 | public static FrameLayout.LayoutParams createFrame(int width, int height, int gravity) {
39 | return new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity);
40 | }
41 |
42 | public static FrameLayout.LayoutParams createFrame(int width, float height) {
43 | return new FrameLayout.LayoutParams(getSize(width), getSize(height));
44 | }
45 |
46 | public static FrameLayout.LayoutParams createFrame(float width, float height, int gravity) {
47 | return new FrameLayout.LayoutParams(getSize(width), getSize(height), gravity);
48 | }
49 |
50 | public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent, int alignRelative, int anchorRelative) {
51 | RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(getSize(width), getSize(height));
52 | if (alignParent >= 0) {
53 | layoutParams.addRule(alignParent);
54 | }
55 | if (alignRelative >= 0 && anchorRelative >= 0) {
56 | layoutParams.addRule(alignRelative, anchorRelative);
57 | }
58 | layoutParams.leftMargin = AndroidUtilities.dp(leftMargin);
59 | layoutParams.topMargin = AndroidUtilities.dp(topMargin);
60 | layoutParams.rightMargin = AndroidUtilities.dp(rightMargin);
61 | layoutParams.bottomMargin = AndroidUtilities.dp(bottomMargin);
62 | return layoutParams;
63 | }
64 |
65 | public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
66 | return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, -1, -1);
67 | }
68 |
69 | public static RelativeLayout.LayoutParams createRelative(int width, int height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignParent) {
70 | return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, alignParent, -1, -1);
71 | }
72 |
73 | public static RelativeLayout.LayoutParams createRelative(float width, float height, int leftMargin, int topMargin, int rightMargin, int bottomMargin, int alignRelative, int anchorRelative) {
74 | return createRelative(width, height, leftMargin, topMargin, rightMargin, bottomMargin, -1, alignRelative, anchorRelative);
75 | }
76 |
77 | public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent, int alignRelative, int anchorRelative) {
78 | return createRelative(width, height, 0, 0, 0, 0, alignParent, alignRelative, anchorRelative);
79 | }
80 |
81 | public static RelativeLayout.LayoutParams createRelative(int width, int height) {
82 | return createRelative(width, height, 0, 0, 0, 0, -1, -1, -1);
83 | }
84 |
85 | public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignParent) {
86 | return createRelative(width, height, 0, 0, 0, 0, alignParent, -1, -1);
87 | }
88 |
89 | public static RelativeLayout.LayoutParams createRelative(int width, int height, int alignRelative, int anchorRelative) {
90 | return createRelative(width, height, 0, 0, 0, 0, -1, alignRelative, anchorRelative);
91 | }
92 |
93 | public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
94 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight);
95 | layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin));
96 | layoutParams.gravity = gravity;
97 | return layoutParams;
98 | }
99 |
100 | public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
101 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight);
102 | layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin));
103 | return layoutParams;
104 | }
105 |
106 | public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity, int leftMargin, int topMargin, int rightMargin, int bottomMargin) {
107 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height));
108 | layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin));
109 | layoutParams.gravity = gravity;
110 | return layoutParams;
111 | }
112 |
113 | public static LinearLayout.LayoutParams createLinear(int width, int height, float leftMargin, float topMargin, float rightMargin, float bottomMargin) {
114 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height));
115 | layoutParams.setMargins(AndroidUtilities.dp(leftMargin), AndroidUtilities.dp(topMargin), AndroidUtilities.dp(rightMargin), AndroidUtilities.dp(bottomMargin));
116 | return layoutParams;
117 | }
118 |
119 | public static LinearLayout.LayoutParams createLinear(int width, int height, float weight, int gravity) {
120 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height), weight);
121 | layoutParams.gravity = gravity;
122 | return layoutParams;
123 | }
124 |
125 | public static LinearLayout.LayoutParams createLinear(int width, int height, int gravity) {
126 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(getSize(width), getSize(height));
127 | layoutParams.gravity = gravity;
128 | return layoutParams;
129 | }
130 |
131 | public static LinearLayout.LayoutParams createLinear(int width, int height, float weight) {
132 | return new LinearLayout.LayoutParams(getSize(width), getSize(height), weight);
133 | }
134 |
135 | public static LinearLayout.LayoutParams createLinear(int width, int height) {
136 | return new LinearLayout.LayoutParams(getSize(width), getSize(height));
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/LineProgressView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This is the source code of Telegram for Android v. 2.0.x.
3 | * It is licensed under GNU GPL v. 2 or later.
4 | * You should have received a copy of the license in this archive (see LICENSE).
5 | *
6 | * Copyright Nikolai Kudashov, 2013-2018.
7 | */
8 |
9 | package com.example.photopaint.views.components;
10 |
11 | import android.content.Context;
12 | import android.graphics.Canvas;
13 | import android.graphics.Paint;
14 | import android.view.View;
15 | import android.view.animation.DecelerateInterpolator;
16 | import com.example.photopaint.helpers.AndroidUtilities;
17 |
18 | public class LineProgressView extends View {
19 |
20 | private long lastUpdateTime;
21 | private float currentProgress;
22 | private float animationProgressStart;
23 | private long currentProgressTime;
24 | private float animatedProgressValue;
25 | private float animatedAlphaValue = 1.0f;
26 |
27 | private int backColor;
28 | private int progressColor;
29 |
30 | private static DecelerateInterpolator decelerateInterpolator;
31 | private static Paint progressPaint;
32 |
33 | public LineProgressView(Context context) {
34 | super(context);
35 |
36 | if (decelerateInterpolator == null) {
37 | decelerateInterpolator = new DecelerateInterpolator();
38 | progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
39 | progressPaint.setStrokeCap(Paint.Cap.ROUND);
40 | progressPaint.setStrokeWidth(AndroidUtilities.dp(2));
41 | }
42 | }
43 |
44 | private void updateAnimation() {
45 | long newTime = System.currentTimeMillis();
46 | long dt = newTime - lastUpdateTime;
47 | lastUpdateTime = newTime;
48 |
49 | if (animatedProgressValue != 1 && animatedProgressValue != currentProgress) {
50 | float progressDiff = currentProgress - animationProgressStart;
51 | if (progressDiff > 0) {
52 | currentProgressTime += dt;
53 | if (currentProgressTime >= 300) {
54 | animatedProgressValue = currentProgress;
55 | animationProgressStart = currentProgress;
56 | currentProgressTime = 0;
57 | } else {
58 | animatedProgressValue = animationProgressStart + progressDiff * decelerateInterpolator.getInterpolation(currentProgressTime / 300.0f);
59 | }
60 | }
61 | invalidate();
62 | }
63 | if (animatedProgressValue >= 1 && animatedProgressValue == 1 && animatedAlphaValue != 0) {
64 | animatedAlphaValue -= dt / 200.0f;
65 | if (animatedAlphaValue <= 0) {
66 | animatedAlphaValue = 0.0f;
67 | }
68 | invalidate();
69 | }
70 | }
71 |
72 | public void setProgressColor(int color) {
73 | progressColor = color;
74 | }
75 |
76 | public void setBackColor(int color) {
77 | backColor = color;
78 | }
79 |
80 | public void setProgress(float value, boolean animated) {
81 | if (!animated) {
82 | animatedProgressValue = value;
83 | animationProgressStart = value;
84 | } else {
85 | animationProgressStart = animatedProgressValue;
86 | }
87 | if (value != 1) {
88 | animatedAlphaValue = 1;
89 | }
90 | currentProgress = value;
91 | currentProgressTime = 0;
92 |
93 | lastUpdateTime = System.currentTimeMillis();
94 | invalidate();
95 | }
96 |
97 | public float getCurrentProgress() {
98 | return currentProgress;
99 | }
100 |
101 | public void onDraw(Canvas canvas) {
102 | if (backColor != 0 && animatedProgressValue != 1) {
103 | progressPaint.setColor(backColor);
104 | progressPaint.setAlpha((int) (255 * animatedAlphaValue));
105 | int start = (int) (getWidth() * animatedProgressValue);
106 | canvas.drawRect(start, 0, getWidth(), getHeight(), progressPaint);
107 | }
108 |
109 | progressPaint.setColor(progressColor);
110 | progressPaint.setAlpha((int)(255 * animatedAlphaValue));
111 | canvas.drawRect(0, 0, getWidth() * animatedProgressValue, getHeight(), progressPaint);
112 | updateAnimation();
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/Point.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components;
2 |
3 | public class Point {
4 | public float x;
5 | public float y;
6 |
7 | public Point() {
8 |
9 | }
10 |
11 | public Point(float x, float y) {
12 | this.x = x;
13 | this.y = y;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/RadialProgressView.java:
--------------------------------------------------------------------------------
1 | /*
2 | * This is the source code of Telegram for Android v. 5.x.x.
3 | * It is licensed under GNU GPL v. 2 or later.
4 | * You should have received a copy of the license in this archive (see LICENSE).
5 | *
6 | * Copyright Nikolai Kudashov, 2013-2018.
7 | */
8 |
9 | package com.example.photopaint.views.components;
10 |
11 | import android.content.Context;
12 | import android.graphics.Canvas;
13 | import android.graphics.Paint;
14 | import android.graphics.RectF;
15 | import android.graphics.drawable.Drawable;
16 | import android.view.View;
17 | import android.view.animation.AccelerateInterpolator;
18 | import android.view.animation.DecelerateInterpolator;
19 | import androidx.annotation.Keep;
20 | import com.example.photopaint.helpers.AndroidUtilities;
21 | import com.example.photopaint.views.actionbar.Theme;
22 |
23 | public class RadialProgressView extends View {
24 |
25 | private long lastUpdateTime;
26 | private float radOffset;
27 | private float currentCircleLength;
28 | private boolean risingCircleLength;
29 | private float currentProgressTime;
30 | private RectF cicleRect = new RectF();
31 | private boolean useSelfAlpha;
32 |
33 | private int progressColor;
34 |
35 | private DecelerateInterpolator decelerateInterpolator;
36 | private AccelerateInterpolator accelerateInterpolator;
37 | private Paint progressPaint;
38 | private static final float rotationTime = 2000;
39 | private static final float risingTime = 500;
40 | private int size;
41 |
42 | public RadialProgressView(Context context) {
43 | super(context);
44 |
45 | size = AndroidUtilities.dp(40);
46 |
47 | progressColor = Theme.getColor(Theme.key_progressCircle);
48 | decelerateInterpolator = new DecelerateInterpolator();
49 | accelerateInterpolator = new AccelerateInterpolator();
50 | progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
51 | progressPaint.setStyle(Paint.Style.STROKE);
52 | progressPaint.setStrokeCap(Paint.Cap.ROUND);
53 | progressPaint.setStrokeWidth(AndroidUtilities.dp(3));
54 | progressPaint.setColor(progressColor);
55 | }
56 |
57 | public void setUseSelfAlpha(boolean value) {
58 | useSelfAlpha = value;
59 | }
60 |
61 | @Keep
62 | @Override
63 | public void setAlpha(float alpha) {
64 | super.setAlpha(alpha);
65 | if (useSelfAlpha) {
66 | Drawable background = getBackground();
67 | int a = (int) (alpha * 255);
68 | if (background != null) {
69 | background.setAlpha(a);
70 | }
71 | progressPaint.setAlpha(a);
72 | }
73 | }
74 |
75 | private void updateAnimation() {
76 | long newTime = System.currentTimeMillis();
77 | long dt = newTime - lastUpdateTime;
78 | if (dt > 17) {
79 | dt = 17;
80 | }
81 | lastUpdateTime = newTime;
82 |
83 | radOffset += 360 * dt / rotationTime;
84 | int count = (int) (radOffset / 360);
85 | radOffset -= count * 360;
86 |
87 | currentProgressTime += dt;
88 | if (currentProgressTime >= risingTime) {
89 | currentProgressTime = risingTime;
90 | }
91 | if (risingCircleLength) {
92 | currentCircleLength = 4 + 266 * accelerateInterpolator.getInterpolation(currentProgressTime / risingTime);
93 | } else {
94 | currentCircleLength = 4 - 270 * (1.0f - decelerateInterpolator.getInterpolation(currentProgressTime / risingTime));
95 | }
96 | if (currentProgressTime == risingTime) {
97 | if (risingCircleLength) {
98 | radOffset += 270;
99 | currentCircleLength = -266;
100 | }
101 | risingCircleLength = !risingCircleLength;
102 | currentProgressTime = 0;
103 | }
104 | invalidate();
105 | }
106 |
107 | public void setSize(int value) {
108 | size = value;
109 | invalidate();
110 | }
111 |
112 | public void setStrokeWidth(float value) {
113 | progressPaint.setStrokeWidth(AndroidUtilities.dp(value));
114 | }
115 |
116 | public void setProgressColor(int color) {
117 | progressColor = color;
118 | progressPaint.setColor(progressColor);
119 | }
120 |
121 | @Override
122 | protected void onDraw(Canvas canvas) {
123 | int x = (getMeasuredWidth() - size) / 2;
124 | int y = (getMeasuredHeight() - size) / 2;
125 | cicleRect.set(x, y, x + size, y + size);
126 | canvas.drawArc(cicleRect, radOffset, currentCircleLength, false, progressPaint);
127 | updateAnimation();
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/Rect.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components;
2 |
3 | public class Rect {
4 |
5 | public float x;
6 | public float y;
7 | public float width;
8 | public float height;
9 |
10 | public Rect() {
11 |
12 | }
13 |
14 | public Rect(float x, float y, float width, float height) {
15 | this.x = x;
16 | this.y = y;
17 | this.width = width;
18 | this.height = height;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/Size.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components;
2 |
3 | public class Size {
4 | public float width;
5 | public float height;
6 |
7 | public Size() {
8 |
9 | }
10 |
11 | public Size(float width, float height) {
12 | this.width = width;
13 | this.height = height;
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/TypefaceSpan.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components;
2 |
3 | import android.graphics.Paint;
4 | import android.graphics.Typeface;
5 | import android.text.TextPaint;
6 | import android.text.style.MetricAffectingSpan;
7 |
8 | import com.example.photopaint.helpers.AndroidUtilities;
9 |
10 | public class TypefaceSpan extends MetricAffectingSpan {
11 |
12 | private Typeface typeface;
13 | private int textSize;
14 | private int color;
15 |
16 | public TypefaceSpan(Typeface tf) {
17 | typeface = tf;
18 | }
19 |
20 | public TypefaceSpan(Typeface tf, int size) {
21 | typeface = tf;
22 | textSize = size;
23 | }
24 |
25 | public TypefaceSpan(Typeface tf, int size, int textColor) {
26 | typeface = tf;
27 | if (size > 0) {
28 | textSize = size;
29 | }
30 | color = textColor;
31 | }
32 |
33 | public Typeface getTypeface() {
34 | return typeface;
35 | }
36 |
37 | public void setColor(int value) {
38 | color = value;
39 | }
40 |
41 | public boolean isMono() {
42 | return typeface == Typeface.MONOSPACE;
43 | }
44 |
45 | public boolean isBold() {
46 | return typeface == AndroidUtilities.getTypeface("fonts/rmedium.ttf");
47 | }
48 |
49 | public boolean isItalic() {
50 | return typeface == AndroidUtilities.getTypeface("fonts/ritalic.ttf");
51 | }
52 |
53 | @Override
54 | public void updateMeasureState(TextPaint p) {
55 | if (typeface != null) {
56 | p.setTypeface(typeface);
57 | }
58 | if (textSize != 0) {
59 | p.setTextSize(textSize);
60 | }
61 | p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
62 | }
63 |
64 | @Override
65 | public void updateDrawState(TextPaint tp) {
66 | if (typeface != null) {
67 | tp.setTypeface(typeface);
68 | }
69 | if (textSize != 0) {
70 | tp.setTextSize(textSize);
71 | }
72 | if (color != 0) {
73 | tp.setColor(color);
74 | }
75 | tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
76 | }
77 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Brush.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.*;
4 | import com.example.photopaint.helpers.ApplicationLoader;
5 | import com.example.photopaint.R;
6 |
7 | public interface Brush {
8 |
9 | float getSpacing();
10 | float getAlpha();
11 | float getAngle();
12 | float getScale();
13 | boolean isLightSaber();
14 | boolean isMosaic();
15 | Bitmap getStamp();
16 |
17 | class Radial implements Brush {
18 |
19 | @Override
20 | public float getSpacing() {
21 | return 0.15f;
22 | }
23 |
24 | @Override
25 | public float getAlpha() {
26 | return 0.85f;
27 | }
28 |
29 | @Override
30 | public float getAngle() {
31 | return 0.0f;
32 | }
33 |
34 | @Override
35 | public float getScale() {
36 | return 1.0f;
37 | }
38 |
39 | @Override
40 | public boolean isLightSaber() {
41 | return false;
42 | }
43 |
44 | @Override
45 | public boolean isMosaic() {
46 | return false;
47 | }
48 |
49 | @Override
50 | public Bitmap getStamp() {
51 | BitmapFactory.Options options = new BitmapFactory.Options();
52 | options.inScaled = false;
53 | return BitmapFactory.decodeResource(ApplicationLoader.applicationContext.getResources(), R.drawable.paint_radial_brush, options);
54 | }
55 | }
56 |
57 | class Elliptical implements Brush {
58 |
59 | @Override
60 | public float getSpacing() {
61 | return 0.04f;
62 | }
63 |
64 | @Override
65 | public float getAlpha() {
66 | return 0.3f;
67 | }
68 |
69 | @Override
70 | public float getAngle() {
71 | return (float) Math.toRadians(125.0);
72 | }
73 |
74 | @Override
75 | public float getScale() {
76 | return 1.5f;
77 | }
78 |
79 | @Override
80 | public boolean isLightSaber() {
81 | return false;
82 | }
83 |
84 | @Override
85 | public boolean isMosaic() {
86 | return false;
87 | }
88 |
89 | @Override
90 | public Bitmap getStamp() {
91 | BitmapFactory.Options options = new BitmapFactory.Options();
92 | options.inScaled = false;
93 | return BitmapFactory.decodeResource(ApplicationLoader.applicationContext.getResources(), R.drawable.paint_elliptical_brush, options);
94 | }
95 | }
96 |
97 | class Neon implements Brush {
98 |
99 | @Override
100 | public float getSpacing() {
101 | return 0.07f;
102 | }
103 |
104 | @Override
105 | public float getAlpha() {
106 | return 0.7f;
107 | }
108 |
109 | @Override
110 | public float getAngle() {
111 | return 0.0f;
112 | }
113 |
114 | @Override
115 | public float getScale() {
116 | return 1.45f;
117 | }
118 |
119 | @Override
120 | public boolean isLightSaber() {
121 | return true;
122 | }
123 |
124 | @Override
125 | public boolean isMosaic() {
126 | return false;
127 | }
128 |
129 | @Override
130 | public Bitmap getStamp() {
131 | BitmapFactory.Options options = new BitmapFactory.Options();
132 | options.inScaled = false;
133 | return BitmapFactory.decodeResource(ApplicationLoader.applicationContext.getResources(), R.drawable.paint_neon_brush, options);
134 | }
135 | }
136 |
137 | class Mosaic implements Brush {
138 |
139 | @Override
140 | public float getSpacing() {
141 | return 0.15f;
142 | }
143 |
144 | @Override
145 | public float getAlpha() {
146 | return 0.85f;
147 | }
148 |
149 | @Override
150 | public float getAngle() {
151 | return 0.0f;
152 | }
153 |
154 | @Override
155 | public float getScale() {
156 | return 3.0f;
157 | }
158 |
159 | @Override
160 | public boolean isLightSaber() {
161 | return false;
162 | }
163 |
164 | @Override
165 | public boolean isMosaic() {
166 | return true;
167 | }
168 |
169 | @Override
170 | public Bitmap getStamp() {
171 | BitmapFactory.Options options = new BitmapFactory.Options();
172 | options.inScaled = false;
173 | return BitmapFactory.decodeResource(ApplicationLoader.applicationContext.getResources(), R.drawable.mosaic_white, options);
174 | }
175 | }
176 |
177 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/GLMatrix.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Matrix;
4 |
5 | public class GLMatrix {
6 |
7 | public static float[] LoadOrtho(float left, float right, float bottom, float top, float near, float far) {
8 | float r_l = right - left;
9 | float t_b = top - bottom;
10 | float f_n = far - near;
11 | float tx = -(right + left) / (right - left);
12 | float ty = -(top + bottom) / (top - bottom);
13 | float tz = -(far + near) / (far - near);
14 |
15 | float out[] = new float[16];
16 |
17 | out[0] = 2.0f / r_l;
18 | out[1] = 0.0f;
19 | out[2] = 0.0f;
20 | out[3] = 0.0f;
21 |
22 | out[4] = 0.0f;
23 | out[5] = 2.0f / t_b;
24 | out[6] = 0.0f;
25 | out[7] = 0.0f;
26 |
27 | out[8] = 0.0f;
28 | out[9] = 0.0f;
29 | out[10] = -2.0f / f_n;
30 | out[11] = 0.0f;
31 |
32 | out[12] = tx;
33 | out[13] = ty;
34 | out[14] = tz;
35 | out[15] = 1.0f;
36 |
37 | return out;
38 | }
39 |
40 | public static float[] LoadGraphicsMatrix(Matrix matrix) {
41 | float m[] = new float[16];
42 | float v[] = new float[9];
43 | matrix.getValues(v);
44 |
45 | m[0] = v[Matrix.MSCALE_X]; //m.a;
46 | m[1] = v[Matrix.MSKEW_X]; //m.b;
47 | m[2] = 0.0f;
48 | m[3] = 0.0f;
49 |
50 | m[4] = v[Matrix.MSKEW_Y]; //m.c;
51 | m[5] = v[Matrix.MSCALE_Y]; //m.d;
52 | m[6] = 0.0f;
53 | m[7] = 0.0f;
54 |
55 | m[8] = 0.0f;
56 | m[9] = 0.0f;
57 | m[10] = 1.0f;
58 | m[11] = 0.0f;
59 |
60 | m[12] = v[Matrix.MTRANS_X]; //m.tx;
61 | m[13] = v[Matrix.MTRANS_Y]; //m.ty;
62 | m[14] = 0.0f;
63 | m[15] = 1.0f;
64 |
65 | return m;
66 | }
67 |
68 | public static float[] MultiplyMat4f(float[] a, float[] b) {
69 | float out[] = new float[16];
70 |
71 | out[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3];
72 | out[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3];
73 | out[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3];
74 | out[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3];
75 |
76 | out[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7];
77 | out[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7];
78 | out[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7];
79 | out[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7];
80 |
81 | out[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11];
82 | out[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11];
83 | out[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11];
84 | out[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11];
85 |
86 | out[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15];
87 | out[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15];
88 | out[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15];
89 | out[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15];
90 |
91 | return out;
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Input.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Matrix;
4 | import android.view.MotionEvent;
5 | import com.example.photopaint.helpers.AndroidUtilities;
6 |
7 | import java.util.Vector;
8 |
9 | public class Input {
10 |
11 | private RenderView renderView;
12 |
13 | private boolean beganDrawing;
14 | private boolean isFirst;
15 | private boolean hasMoved;
16 | private boolean clearBuffer;
17 |
18 | private Point lastLocation;
19 | private double lastRemainder;
20 |
21 | private Point[] points = new Point[3];
22 | private int pointsCount;
23 |
24 | private Matrix invertMatrix;
25 | private float[] tempPoint = new float[2];
26 |
27 | public Input(RenderView render) {
28 | renderView = render;
29 | }
30 |
31 | public void setMatrix(Matrix m) {
32 | invertMatrix = new Matrix();
33 | m.invert(invertMatrix);
34 | }
35 |
36 | public void process(MotionEvent event) {
37 | int action = event.getActionMasked();
38 | float x = event.getX();
39 | float y = renderView.getHeight() - event.getY();
40 |
41 | tempPoint[0] = x;
42 | tempPoint[1] = y;
43 | invertMatrix.mapPoints(tempPoint);
44 |
45 | Point location = new Point(tempPoint[0], tempPoint[1], 1.0f);
46 |
47 | switch (action) {
48 | case MotionEvent.ACTION_DOWN:
49 | case MotionEvent.ACTION_MOVE: {
50 | if (!beganDrawing) {
51 | // 如果当前的状态不是正在绘制中,那么开始绘制
52 | beganDrawing = true;
53 | hasMoved = false;
54 | isFirst = true;
55 |
56 | // 记录上一个点
57 | lastLocation = location;
58 |
59 | points[0] = location;
60 | pointsCount = 1;
61 |
62 | clearBuffer = true;
63 | } else {
64 | //每次移动都是直线距离(无数个短的直线形成曲线),这里的distance是三维直线距离
65 | float distance = location.getDistanceTo(lastLocation);
66 | if (distance < AndroidUtilities.dp(5.0f)) {
67 | // 移动距离小于5dp时,绘制状态不做变更
68 | return;
69 | }
70 |
71 | if (!hasMoved) {
72 | // 当发生移动之后回调onBeganDrawing
73 | renderView.onBeganDrawing();
74 | hasMoved = true;
75 | }
76 |
77 | // 把当前点记录下来
78 | points[pointsCount] = location;
79 | pointsCount++;
80 |
81 | if (pointsCount == 3) {
82 | //中间添加过渡的点让路径path更顺滑,并绘制这些点
83 | smoothenAndPaintPoints(false);
84 | }
85 |
86 | lastLocation = location;
87 | }
88 | }
89 | break;
90 |
91 | case MotionEvent.ACTION_UP: {
92 | if (!hasMoved) {
93 | if (renderView.shouldDraw()) {
94 | location.edge = true;
95 | paintPath(new Path(location));
96 | }
97 | reset();
98 | } else if (pointsCount > 0) {
99 | smoothenAndPaintPoints(true);
100 | }
101 |
102 | pointsCount = 0;
103 |
104 | renderView.getPainting().commitStroke(renderView.getCurrentColor());
105 | beganDrawing = false;
106 |
107 | renderView.onFinishedDrawing(hasMoved);
108 | }
109 | break;
110 | }
111 | }
112 |
113 | private void reset() {
114 | pointsCount = 0;
115 | }
116 |
117 | private void smoothenAndPaintPoints(boolean ended) {
118 | if (pointsCount > 2) {
119 | Vector points = new Vector<>();
120 |
121 | Point prev2 = this.points[0];
122 | Point prev1 = this.points[1];
123 | Point cur = this.points[2];
124 |
125 | if (cur == null || prev1 == null || prev2 == null) {
126 | return;
127 | }
128 |
129 | Point midPoint1 = prev1.multiplySum(prev2, 0.5f);// 计算缓存的前两个点的中间点
130 | midPoint1.mosaicColor = renderView.getMosaicColor((float) midPoint1.x, (float) midPoint1.y);
131 | Point midPoint2 = cur.multiplySum(prev1, 0.5f);// 计算当前点和上一个点的中间点
132 | midPoint2.mosaicColor = renderView.getMosaicColor((float) midPoint2.x, (float) midPoint2.y);
133 |
134 | int segmentDistance = renderView.isMosaic() ? 30 : 1;//设置线段的距离为1px.
135 | float distance = midPoint1.getDistanceTo(midPoint2);// 计算两个中间点的距离
136 | int numberOfSegments = (int) Math.min(48, Math.max(Math.floor(distance / segmentDistance), 24));// 计算可以分成多少段
137 |
138 | float t = 0.0f;
139 | float step = 1.0f / (float) numberOfSegments;
140 |
141 | for (int j = 0; j < numberOfSegments; j++) {
142 | Point point = smoothPoint(midPoint1, midPoint2, prev1, t);// 添加过渡点
143 | point.mosaicColor = renderView.getMosaicColor((float) point.x, (float) point.y);
144 | if (isFirst) {// 是否为起始点,如果是起始点和结束点都需要标记
145 | point.edge = true;
146 | isFirst = false;
147 | }
148 | points.add(point);
149 | t += step;
150 | }
151 |
152 | if (ended) {// 是否为结束点,当手势抬起的时候标记为结束点
153 | midPoint2.edge = true;
154 | }
155 | points.add(midPoint2);
156 |
157 | Point[] result = new Point[points.size()];
158 | points.toArray(result);
159 |
160 | Path path = new Path(result);
161 | paintPath(path);
162 |
163 | System.arraycopy(this.points, 1, this.points, 0, 2);
164 |
165 | if (ended) {
166 | pointsCount = 0;
167 | } else {
168 | pointsCount = 2;
169 | }
170 | }
171 | else {
172 | Point[] result = new Point[pointsCount];
173 | System.arraycopy(this.points, 0, result, 0, pointsCount);
174 | Path path = new Path(result);
175 | paintPath(path);
176 | }
177 | }
178 |
179 | private Point smoothPoint(Point midPoint1, Point midPoint2, Point prev1, float t) {
180 | double a1 = Math.pow(1.0f - t, 2);
181 | double a2 = (2.0f * (1.0f - t) * t);
182 | double a3 = t * t;
183 |
184 | return new Point(midPoint1.x * a1 + prev1.x * a2 + midPoint2.x * a3, midPoint1.y * a1 + prev1.y * a2 + midPoint2.y * a3, 1.0f);
185 | }
186 |
187 | private void paintPath(final Path path) {
188 | path.setup(renderView.getCurrentColor(), renderView.getCurrentWeight(), renderView.getCurrentBrush());
189 |
190 | if (clearBuffer) {
191 | lastRemainder = 0.0f;
192 | }
193 |
194 | path.remainder = lastRemainder;// 接续上个path
195 |
196 | renderView.getPainting().paintStroke(path, clearBuffer, new Runnable() {
197 | @Override
198 | public void run() {
199 | AndroidUtilities.runOnUIThread(new Runnable() {
200 | @Override
201 | public void run() {
202 | lastRemainder = path.remainder;
203 | clearBuffer = false;
204 | }
205 | });
206 | }
207 | });
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Path.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import java.util.Arrays;
4 | import java.util.Vector;
5 |
6 | public class Path {
7 |
8 | public double remainder;
9 | private Vector points = new Vector<>();
10 | private int color;
11 | private float baseWeight;
12 | private Brush brush;
13 |
14 | public Path(Point point) {
15 | points.add(point);
16 | }
17 |
18 | public Path(Point[] points) {
19 | this.points.addAll(Arrays.asList(points));
20 | }
21 |
22 | public int getLength() {
23 | if (points == null) {
24 | return 0;
25 | }
26 | return points.size();
27 | }
28 |
29 | public Point[] getPoints() {
30 | Point[] points = new Point[this.points.size()];
31 | this.points.toArray(points);
32 | return points;
33 | }
34 |
35 | public int getColor() {
36 | return color;
37 | }
38 |
39 | public float getBaseWeight() {
40 | return baseWeight;
41 | }
42 |
43 | public Brush getBrush() {
44 | return brush;
45 | }
46 |
47 | public void setup(int color, float baseWeight, Brush brush) {
48 | this.color = color;
49 | this.baseWeight = baseWeight;
50 | this.brush = brush;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/PhotoFace.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.PointF;
5 | import com.google.android.gms.vision.face.Face;
6 | import com.google.android.gms.vision.face.Landmark;
7 | import com.example.photopaint.views.components.Size;
8 | import com.example.photopaint.views.components.Point;
9 |
10 | import java.util.List;
11 |
12 | public class PhotoFace {
13 |
14 | private float width;
15 | private float angle;
16 |
17 | private Point foreheadPoint;
18 |
19 | private Point eyesCenterPoint;
20 | private float eyesDistance;
21 |
22 | private Point mouthPoint;
23 | private Point chinPoint;
24 |
25 | public PhotoFace(Face face, Bitmap sourceBitmap, Size targetSize, boolean sideward) {
26 | List landmarks = face.getLandmarks();
27 |
28 | Point leftEyePoint = null;
29 | Point rightEyePoint = null;
30 |
31 | Point leftMouthPoint = null;
32 | Point rightMouthPoint = null;
33 |
34 | for (Landmark landmark : landmarks) {
35 | PointF point = landmark.getPosition();
36 |
37 | switch (landmark.getType()) {
38 | case Landmark.LEFT_EYE: {
39 | leftEyePoint = transposePoint(point, sourceBitmap, targetSize, sideward);
40 | }
41 | break;
42 |
43 | case Landmark.RIGHT_EYE: {
44 | rightEyePoint = transposePoint(point, sourceBitmap, targetSize, sideward);
45 | }
46 | break;
47 |
48 | case Landmark.LEFT_MOUTH: {
49 | leftMouthPoint = transposePoint(point, sourceBitmap, targetSize, sideward);
50 | }
51 | break;
52 |
53 | case Landmark.RIGHT_MOUTH: {
54 | rightMouthPoint = transposePoint(point, sourceBitmap, targetSize, sideward);
55 | }
56 | break;
57 | }
58 | }
59 |
60 | if (leftEyePoint != null && rightEyePoint != null) {
61 | eyesCenterPoint = new Point(0.5f * leftEyePoint.x + 0.5f * rightEyePoint.x,
62 | 0.5f * leftEyePoint.y + 0.5f * rightEyePoint.y);
63 | eyesDistance = (float)Math.hypot(rightEyePoint.x - leftEyePoint.x, rightEyePoint.y - leftEyePoint.y);
64 | angle = (float)Math.toDegrees(Math.PI + Math.atan2(rightEyePoint.y - leftEyePoint.y, rightEyePoint.x - leftEyePoint.x));
65 |
66 | width = eyesDistance * 2.35f;
67 |
68 | float foreheadHeight = 0.8f * eyesDistance;
69 | float upAngle = (float)Math.toRadians(angle - 90);
70 | foreheadPoint = new Point(eyesCenterPoint.x + foreheadHeight * (float)Math.cos(upAngle),
71 | eyesCenterPoint.y + foreheadHeight * (float)Math.sin(upAngle));
72 | }
73 |
74 | if (leftMouthPoint != null && rightMouthPoint != null) {
75 | mouthPoint = new Point(0.5f * leftMouthPoint.x + 0.5f * rightMouthPoint.x,
76 | 0.5f * leftMouthPoint.y + 0.5f * rightMouthPoint.y);
77 |
78 | float chinDepth = 0.7f * eyesDistance;
79 | float downAngle = (float)Math.toRadians(angle + 90);
80 | chinPoint = new Point(mouthPoint.x + chinDepth * (float)Math.cos(downAngle),
81 | mouthPoint.y + chinDepth * (float)Math.sin(downAngle));
82 | }
83 | }
84 |
85 | public boolean isSufficient() {
86 | return eyesCenterPoint != null;
87 | }
88 |
89 | private Point transposePoint(PointF point, Bitmap sourceBitmap, Size targetSize, boolean sideward) {
90 | float bitmapW = sideward ? sourceBitmap.getHeight() : sourceBitmap.getWidth();
91 | float bitmapH = sideward ? sourceBitmap.getWidth() : sourceBitmap.getHeight();
92 | return new Point(targetSize.width * point.x / bitmapW,
93 | targetSize.height * point.y / bitmapH);
94 | }
95 |
96 | public Point getPointForAnchor(int anchor) {
97 | switch (anchor) {
98 | case 0: {
99 | return foreheadPoint;
100 | }
101 |
102 | case 1: {
103 | return eyesCenterPoint;
104 | }
105 |
106 | case 2: {
107 | return mouthPoint;
108 | }
109 |
110 | case 3: {
111 | return chinPoint;
112 | }
113 |
114 | default: {
115 | return null;
116 | }
117 | }
118 | }
119 |
120 | public float getWidthForAnchor(int anchor) {
121 | if (anchor == 1)
122 | return eyesDistance;
123 |
124 | return width;
125 | }
126 |
127 | public float getAngle() {
128 | return angle;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Point.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.PointF;
5 |
6 | public class Point {
7 |
8 | public double x;
9 | public double y;
10 | public double z;
11 |
12 | public boolean edge;
13 | public int mosaicColor;
14 |
15 | public Point(double x, double y, double z) {
16 | this.x = x;
17 | this.y = y;
18 | this.z = z;
19 | }
20 |
21 | public Point(Point point) {
22 | x = point.x;
23 | y = point.y;
24 | z = point.z;
25 | }
26 |
27 | @Override
28 | public boolean equals(Object obj) {
29 | if (obj == null) {
30 | return false;
31 | }
32 | if (obj == this) {
33 | return true;
34 | }
35 | if (!(obj instanceof Point)) {
36 | return false;
37 | }
38 | Point other = (Point) obj;
39 | return this.x == other.x && this.y == other.y && this.z == other.z;
40 | }
41 |
42 | Point multiplySum(Point point, double scalar) {
43 | return new Point((x + point.x) * scalar, (y + point.y) * scalar, (z + point.z) * scalar);
44 | }
45 |
46 | Point multiplyAndAdd(double scalar, Point point) {
47 | return new Point((x * scalar) + point.x, (y * scalar) + point.y, (z * scalar) + point.z);
48 | }
49 |
50 | private int getSmoothColor(Point point, double scalar){
51 | int r1 = Color.red(mosaicColor);
52 | int g1 = Color.green(mosaicColor);
53 | int b1 = Color.blue(mosaicColor);
54 | int a1 = Color.alpha(mosaicColor);
55 |
56 | int r2 = Color.red(point.getMosaicColor());
57 | int g2 = Color.green(point.getMosaicColor());
58 | int b2 = Color.blue(point.getMosaicColor());
59 | int a2 = Color.alpha(point.getMosaicColor());
60 |
61 | int r = (int) Math.min(255,(r1 + r2) * scalar);
62 | int g = (int) Math.min(255,(g1 + g2) * scalar);
63 | int b = (int) Math.min(255,(b1 + b2) * scalar);
64 | int a = (int) Math.min(255,(a1 + a2) * scalar);
65 |
66 | return Color.argb(a, r, g, b);
67 | }
68 |
69 | private int addColor(int color1, int color2){
70 | int r1 = Color.red(color1);
71 | int g1 = Color.green(color1);
72 | int b1 = Color.blue(color1);
73 | int a1 = Color.alpha(color1);
74 |
75 | int r2 = Color.red(color2);
76 | int g2 = Color.green(color2);
77 | int b2 = Color.blue(color2);
78 | int a2 = Color.alpha(color2);
79 |
80 | int r = Math.min(255,(r1 + r2));
81 | int g = Math.min(255,(g1 + g2));
82 | int b = Math.min(255,(b1 + b2));
83 | int a = Math.min(255,(a1 + a2));
84 |
85 | return Color.argb(a, r, g, b);
86 | }
87 |
88 | private int substractColor(int color1, int color2){
89 | int r1 = Color.red(color1);
90 | int g1 = Color.green(color1);
91 | int b1 = Color.blue(color1);
92 | int a1 = Color.alpha(color1);
93 |
94 | int r2 = Color.red(color2);
95 | int g2 = Color.green(color2);
96 | int b2 = Color.blue(color2);
97 | int a2 = Color.alpha(color2);
98 |
99 | int r = Math.max(0,(r1 - r2));
100 | int g = Math.max(0,(g1 - g2));
101 | int b = Math.max(0,(b1 - b2));
102 | int a = Math.max(0,(a1 - a2));
103 |
104 | return Color.argb(a, r, g, b);
105 | }
106 |
107 | void alteringAddMultiplication(Point point, double scalar) {
108 | x = x + (point.x * scalar);
109 | y = y + (point.y * scalar);
110 | z = z + (point.z * scalar);
111 | }
112 |
113 | Point add(Point point) {
114 | return new Point(x + point.x, y + point.y, z + point.z);
115 | }
116 |
117 | Point substract(Point point) {
118 | return new Point(x - point.x, y - point.y, z - point.z);
119 | }
120 |
121 | Point multiplyByScalar(double scalar) {
122 | return new Point(x * scalar, y * scalar, z * scalar);
123 | }
124 |
125 | Point getNormalized() {
126 | return multiplyByScalar(1.0 / getMagnitude());
127 | }
128 |
129 | private double getMagnitude() {
130 | return Math.sqrt(x * x + y * y + z * z);
131 | }
132 |
133 | float getDistanceTo(Point point) {
134 | return (float) Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2) + Math.pow(z - point.z, 2));
135 | }
136 |
137 | PointF toPointF() {
138 | return new PointF((float) x, (float) y);
139 | }
140 |
141 | public int getMosaicColor(){
142 | return this.mosaicColor;
143 | }
144 | }
145 |
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Render.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.Matrix;
5 | import android.graphics.PointF;
6 | import android.graphics.RectF;
7 | import android.opengl.GLES20;
8 |
9 | import java.nio.ByteBuffer;
10 | import java.nio.ByteOrder;
11 | import java.nio.FloatBuffer;
12 |
13 | public class Render {
14 |
15 | public static RectF RenderPath(Path path, RenderState state) {
16 | state.baseWeight = path.getBaseWeight();
17 | state.spacing = path.getBrush().getSpacing();
18 | state.alpha = path.getBrush().getAlpha();
19 | state.angle = path.getBrush().getAngle();
20 | state.scale = path.getBrush().getScale();
21 | // 给马赛克画刷赋默认值
22 | state.red = 0.0f;
23 | state.green = 0.0f;
24 | state.blue = 0.0f;
25 | state.z = path.getBrush().isMosaic() ? -1f : 1f;
26 |
27 | int length = path.getLength();
28 | if (length == 0) {
29 | return null;
30 | }
31 |
32 | if (length == 1) {
33 | // 如果是点就绘制Stamp
34 | if(path.getBrush().isMosaic()) {
35 | // 如果是马赛克就给画笔颜色重新赋值
36 | state.red = (float) (Color.red(path.getPoints()[0].getMosaicColor())) / 255;
37 | state.green = (float) (Color.green(path.getPoints()[0].getMosaicColor())) / 255;
38 | state.blue = (float) (Color.blue(path.getPoints()[0].getMosaicColor())) / 255;
39 | state.alpha = (float) (Color.alpha(path.getPoints()[0].getMosaicColor())) / 255;
40 | }
41 |
42 | PaintStamp(path.getPoints()[0], state);
43 | } else {
44 | // 如果是线就绘制点与点之间的线段
45 | Point[] points = path.getPoints();
46 | state.prepare();
47 |
48 | for (int i = 0; i < points.length - 1; i++) {
49 | if(path.getBrush().isMosaic()) {
50 | // 如果是马赛克就给画笔颜色重新赋值
51 | state.red = (float) (Color.red(path.getPoints()[i].getMosaicColor())) / 255;
52 | state.green = (float) (Color.green(path.getPoints()[i].getMosaicColor())) / 255;
53 | state.blue = (float) (Color.blue(path.getPoints()[i].getMosaicColor())) / 255;
54 | state.alpha = (float) (Color.alpha(path.getPoints()[1].getMosaicColor())) / 255;
55 | }
56 | PaintSegment(points[i], points[i + 1], state);
57 | }
58 | }
59 |
60 | path.remainder = state.remainder;
61 |
62 | return Draw(state);
63 | }
64 |
65 | private static void PaintSegment(Point lastPoint, Point point, RenderState state) {
66 | double distance = lastPoint.getDistanceTo(point);
67 | Point vector = point.substract(lastPoint);
68 | Point unitVector = new Point(1.0f, 1.0f, 0.0f);
69 | float vectorAngle = Math.abs(state.angle) > 0.0f ? state.angle : (float) Math.atan2(vector.y, vector.x);
70 |
71 | float brushWeight = state.baseWeight * state.scale;
72 | double step = Math.max(1.0f, state.spacing * brushWeight);
73 |
74 | if (distance > 0.0) {
75 | unitVector = vector.multiplyByScalar(1.0 / distance);
76 | }
77 |
78 | float boldenedAlpha = Math.min(1.0f, state.alpha * 1.15f);
79 | boolean boldenHead = lastPoint.edge;
80 | boolean boldenTail = point.edge;
81 |
82 | int count = (int) Math.ceil((distance - state.remainder) / step);
83 | int currentCount = state.getCount();
84 | state.appendValuesCount(count);
85 | state.setPosition(currentCount);
86 |
87 | Point start = lastPoint.add(unitVector.multiplyByScalar(state.remainder));
88 |
89 | boolean succeed = true;
90 | double f = state.remainder;
91 | for (; f <= distance; f += step) {
92 | float alpha = boldenHead ? boldenedAlpha : state.alpha;
93 | succeed = state.addPoint(start.toPointF(), brushWeight, vectorAngle, alpha, -1);
94 | if (!succeed) {
95 | break;
96 | }
97 |
98 | start = start.add(unitVector.multiplyByScalar(step));
99 | boldenHead = false;
100 | }
101 |
102 | if (succeed && boldenTail) {
103 | state.appendValuesCount(1);
104 | state.addPoint(point.toPointF(), brushWeight, vectorAngle, boldenedAlpha, -1);
105 | }
106 |
107 | state.remainder = f - distance;
108 | }
109 |
110 | private static void PaintStamp(Point point, RenderState state) {
111 | float brushWeight = state.baseWeight * state.scale;
112 | PointF start = point.toPointF();
113 | float angle = Math.abs(state.angle) > 0.0f ? state.angle : 0.0f;
114 | float alpha = state.alpha;
115 |
116 | state.prepare();
117 | state.appendValuesCount(1);
118 | state.addPoint(start, brushWeight, angle, alpha, 0);
119 | }
120 |
121 | private static RectF Draw(RenderState state) {
122 | RectF dataBounds = new RectF(0, 0, 0, 0);
123 |
124 | int count = state.getCount();
125 | if (count == 0) {
126 | return dataBounds;
127 | }
128 |
129 | int vertexDataSize = 9 * Float.SIZE / 8;
130 | int capacity = vertexDataSize * (count * 4 + (count - 1) * 2);
131 | ByteBuffer bb = ByteBuffer.allocateDirect(capacity);
132 | bb.order(ByteOrder.nativeOrder());
133 | FloatBuffer vertexData = bb.asFloatBuffer();
134 | vertexData.position(0);
135 | state.setPosition(0);
136 |
137 | int n = 0;
138 | for (int i = 0; i < count; i++) {
139 | float x = state.read();
140 | float y = state.read();
141 | float z = state.read();
142 | float size = state.read();
143 | float angle = state.read();
144 | float alpha = state.read();
145 | float red = state.read();
146 | float green = state.read();
147 | float blue = state.read();
148 |
149 | RectF rect = new RectF(x - size, y - size, x + size, y + size);
150 | float[] points = new float[]{
151 | rect.left, rect.top,
152 | rect.right, rect.top,
153 | rect.left, rect.bottom,
154 | rect.right, rect.bottom
155 | };
156 |
157 | float centerX = rect.centerX();
158 | float centerY = rect.centerY();
159 |
160 | Matrix t = new Matrix();
161 | t.setRotate((float) Math.toDegrees(angle), centerX, centerY);
162 | t.mapPoints(points);
163 | t.mapRect(rect);
164 |
165 | Utils.RectFIntegral(rect);
166 | dataBounds.union(rect);
167 |
168 | if (n != 0) {
169 | vertexData.put(points[0]);
170 | vertexData.put(points[1]);
171 | vertexData.put(z);
172 | vertexData.put(0);
173 | vertexData.put(0);
174 | vertexData.put(alpha);
175 | vertexData.put(red);
176 | vertexData.put(green);
177 | vertexData.put(blue);
178 | n++;
179 | }
180 |
181 | vertexData.put(points[0]);
182 | vertexData.put(points[1]);
183 | vertexData.put(z);
184 | vertexData.put(0);
185 | vertexData.put(0);
186 | vertexData.put(alpha);
187 | vertexData.put(red);
188 | vertexData.put(green);
189 | vertexData.put(blue);
190 | n++;
191 |
192 | vertexData.put(points[2]);
193 | vertexData.put(points[3]);
194 | vertexData.put(z);
195 | vertexData.put(1);
196 | vertexData.put(0);
197 | vertexData.put(alpha);
198 | vertexData.put(red);
199 | vertexData.put(green);
200 | vertexData.put(blue);
201 | n++;
202 |
203 | vertexData.put(points[4]);
204 | vertexData.put(points[5]);
205 | vertexData.put(z);
206 | vertexData.put(0);
207 | vertexData.put(1);
208 | vertexData.put(alpha);
209 | vertexData.put(red);
210 | vertexData.put(green);
211 | vertexData.put(blue);
212 | n++;
213 |
214 | vertexData.put(points[6]);
215 | vertexData.put(points[7]);
216 | vertexData.put(z);
217 | vertexData.put(1);
218 | vertexData.put(1);
219 | vertexData.put(alpha);
220 | vertexData.put(red);
221 | vertexData.put(green);
222 | vertexData.put(blue);
223 | n++;
224 |
225 | if (i != count - 1) {
226 | vertexData.put(points[6]);
227 | vertexData.put(points[7]);
228 | vertexData.put(z);
229 | vertexData.put(1);
230 | vertexData.put(1);
231 | vertexData.put(alpha);
232 | vertexData.put(red);
233 | vertexData.put(green);
234 | vertexData.put(blue);
235 | n++;
236 | }
237 | }
238 |
239 | vertexData.position(0);
240 | FloatBuffer coordData = vertexData.slice();
241 | GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, vertexDataSize, coordData);
242 | GLES20.glEnableVertexAttribArray(0);
243 |
244 | vertexData.position(3);
245 | FloatBuffer texData = vertexData.slice();
246 | GLES20.glVertexAttribPointer(1, 2, GLES20.GL_FLOAT, true, vertexDataSize, texData);
247 | GLES20.glEnableVertexAttribArray(1);
248 |
249 | vertexData.position(5);
250 | FloatBuffer alphaData = vertexData.slice();
251 | GLES20.glVertexAttribPointer(2, 1, GLES20.GL_FLOAT, true, vertexDataSize, alphaData);
252 | GLES20.glEnableVertexAttribArray(2);
253 |
254 | vertexData.position(6);
255 | FloatBuffer redData = vertexData.slice();
256 | GLES20.glVertexAttribPointer(3, 1, GLES20.GL_FLOAT, true, vertexDataSize, redData);
257 | GLES20.glEnableVertexAttribArray(3);
258 |
259 | vertexData.position(7);
260 | FloatBuffer greenData = vertexData.slice();
261 | GLES20.glVertexAttribPointer(4, 1, GLES20.GL_FLOAT, true, vertexDataSize, greenData);
262 | GLES20.glEnableVertexAttribArray(4);
263 |
264 | vertexData.position(8);
265 | FloatBuffer blueData = vertexData.slice();
266 | GLES20.glVertexAttribPointer(5, 1, GLES20.GL_FLOAT, true, vertexDataSize, blueData);
267 | GLES20.glEnableVertexAttribArray(5);
268 |
269 |
270 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, n);
271 |
272 | return dataBounds;
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/RenderState.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.PointF;
4 |
5 | import java.nio.ByteBuffer;
6 | import java.nio.ByteOrder;
7 |
8 | public class RenderState {
9 | private static final int DEFAULT_STATE_SIZE = 256;
10 |
11 | public float baseWeight;
12 | public float spacing;
13 | public float alpha;
14 | public float angle;
15 | public float scale;
16 | public float red;
17 | public float green;
18 | public float blue;
19 | public float z;
20 |
21 | public double remainder;
22 |
23 | private int count;
24 | private int allocatedCount;
25 | private ByteBuffer buffer;
26 |
27 | public int getCount() {
28 | return count;
29 | }
30 |
31 | public void prepare() {
32 | count = 0;
33 |
34 | if (buffer != null) {
35 | return;
36 | }
37 |
38 | allocatedCount = DEFAULT_STATE_SIZE;
39 | buffer = ByteBuffer.allocateDirect(allocatedCount * 9 * 4);
40 | buffer.order(ByteOrder.nativeOrder());
41 | buffer.position(0);
42 | }
43 |
44 | public float read() {
45 | return buffer.getFloat();
46 | }
47 |
48 | public void setPosition(int position) {
49 | if (buffer == null || position < 0 || position >= allocatedCount) {
50 | return;
51 | }
52 | buffer.position(position * 9 * 4);
53 | }
54 |
55 | public void appendValuesCount(int count) {
56 | int newTotalCount = this.count + count;
57 |
58 | if (newTotalCount > allocatedCount || buffer == null) {
59 | resizeBuffer();
60 | }
61 |
62 | this.count = newTotalCount;
63 | }
64 |
65 | public void resizeBuffer() {
66 | if (buffer != null) {
67 | buffer = null;
68 | }
69 |
70 | allocatedCount = Math.max(allocatedCount * 2, DEFAULT_STATE_SIZE);
71 |
72 | buffer = ByteBuffer.allocateDirect(allocatedCount * 9 * 4);
73 | buffer.order(ByteOrder.nativeOrder());
74 | buffer.position(0);
75 | }
76 |
77 | public boolean addPoint(PointF point, float size, float angle, float alpha, int index) {
78 | if (index != -1 && index >= allocatedCount || buffer.position() == buffer.limit()) {
79 | resizeBuffer();
80 | return false;
81 | }
82 |
83 | if (index != -1) {
84 | buffer.position(index * 9 * 4);
85 | }
86 | buffer.putFloat(point.x);
87 | buffer.putFloat(point.y);
88 | buffer.putFloat(z);
89 | buffer.putFloat(size);
90 | buffer.putFloat(angle);
91 | buffer.putFloat(alpha);
92 | buffer.putFloat(red);
93 | buffer.putFloat(green);
94 | buffer.putFloat(blue);
95 |
96 | return true;
97 | }
98 |
99 | public void reset() {
100 | count = 0;
101 | remainder = 0;
102 | if (buffer != null) {
103 | buffer.position(0);
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Shader.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Color;
4 | import android.opengl.GLES20;
5 | import com.example.photopaint.helpers.BuildVars;
6 | import com.example.photopaint.helpers.FileLog;
7 |
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class Shader {
12 |
13 | protected int program;
14 | private int vertexShader;
15 | private int fragmentShader;
16 |
17 | protected Map uniformsMap = new HashMap<>();
18 |
19 | public Shader(String vertexShader, String fragmentShader, String attributes[], String uniforms[]) {
20 | this.program = GLES20.glCreateProgram();
21 |
22 | CompilationResult vResult = compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
23 | if (vResult.status == GLES20.GL_FALSE) {
24 | if (BuildVars.LOGS_ENABLED) {
25 | FileLog.e("Vertex shader compilation failed");
26 | }
27 | destroyShader(vResult.shader, 0, program);
28 | return;
29 | }
30 |
31 | CompilationResult fResult = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
32 | if (fResult.status == GLES20.GL_FALSE) {
33 | if (BuildVars.LOGS_ENABLED) {
34 | FileLog.e("Fragment shader compilation failed");
35 | }
36 | destroyShader(vResult.shader, fResult.shader, program);
37 | return;
38 | }
39 |
40 | GLES20.glAttachShader(program, vResult.shader);
41 | GLES20.glAttachShader(program, fResult.shader);
42 |
43 | for (int i = 0; i < attributes.length; i++) {
44 | GLES20.glBindAttribLocation(program, i, attributes[i]);
45 | }
46 |
47 | if (linkProgram(program) == GLES20.GL_FALSE) {
48 | destroyShader(vResult.shader, fResult.shader, program);
49 | return;
50 | }
51 |
52 | for (String uniform : uniforms) {
53 | uniformsMap.put(uniform, GLES20.glGetUniformLocation(program, uniform));
54 | }
55 |
56 | if (vResult.shader != 0) {
57 | GLES20.glDeleteShader(vResult.shader);
58 | }
59 |
60 | if (fResult.shader != 0) {
61 | GLES20.glDeleteShader(fResult.shader);
62 | }
63 | }
64 |
65 | public void cleanResources() {
66 | if (program != 0) {
67 | GLES20.glDeleteProgram(vertexShader);
68 | program = 0;
69 | }
70 | }
71 |
72 | private class CompilationResult {
73 | int shader;
74 | int status;
75 |
76 | CompilationResult(int shader, int status) {
77 | this.shader = shader;
78 | this.status = status;
79 | }
80 | }
81 |
82 | public int getUniform(String key) {
83 | return uniformsMap.get(key);
84 | }
85 |
86 | private CompilationResult compileShader(int type, String shaderCode) {
87 | int shader = GLES20.glCreateShader(type);
88 | GLES20.glShaderSource(shader, shaderCode);
89 | GLES20.glCompileShader(shader);
90 |
91 | int[] compileStatus = new int[1];
92 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
93 | if (compileStatus[0] == GLES20.GL_FALSE) {
94 | if (BuildVars.LOGS_ENABLED) {
95 | FileLog.e(GLES20.glGetShaderInfoLog(shader));
96 | }
97 | }
98 |
99 | return new CompilationResult(shader, compileStatus[0]);
100 | }
101 |
102 | private int linkProgram(int program) {
103 | GLES20.glLinkProgram(program);
104 |
105 | int[] linkStatus = new int[1];
106 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
107 | if (linkStatus[0] == GLES20.GL_FALSE) {
108 | if (BuildVars.LOGS_ENABLED) {
109 | FileLog.e(GLES20.glGetProgramInfoLog(program));
110 | }
111 | }
112 |
113 | return linkStatus[0];
114 | }
115 |
116 | private void destroyShader(int vertexShader, int fragmentShader, int program) {
117 | if (vertexShader != 0) {
118 | GLES20.glDeleteShader(vertexShader);
119 | }
120 |
121 | if (fragmentShader != 0) {
122 | GLES20.glDeleteShader(fragmentShader);
123 | }
124 |
125 | if (program != 0) {
126 | GLES20.glDeleteProgram(vertexShader);
127 | }
128 | }
129 |
130 | public static void SetColorUniform(int location, int color) {
131 | float r = Color.red(color) / 255.0f;
132 | float g = Color.green(color) / 255.0f;
133 | float b = Color.blue(color) / 255.0f;
134 | float a = Color.alpha(color) / 255.0f;
135 |
136 | GLES20.glUniform4f(location, r, g, b, a);
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Slice.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.RectF;
4 | import com.example.photopaint.helpers.ApplicationLoader;
5 | import com.example.photopaint.helpers.DispatchQueue;
6 | import com.example.photopaint.helpers.FileLog;
7 |
8 | import java.io.ByteArrayOutputStream;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.FileOutputStream;
12 | import java.nio.ByteBuffer;
13 | import java.util.zip.Deflater;
14 | import java.util.zip.Inflater;
15 |
16 | public class Slice {
17 | private RectF bounds;
18 | private File file;
19 |
20 | public Slice(final ByteBuffer data, RectF rect, DispatchQueue queue) {
21 | bounds = rect;
22 |
23 | try {
24 | File outputDir = ApplicationLoader.applicationContext.getCacheDir();
25 | file = File.createTempFile("paint", ".bin", outputDir);
26 | } catch (Exception e) {
27 | FileLog.e(e);
28 | }
29 |
30 | if (file == null)
31 | return;
32 |
33 | storeData(data);
34 | }
35 |
36 | public void cleanResources() {
37 | if (file != null) {
38 | file.delete();
39 | file = null;
40 | }
41 | }
42 |
43 | private void storeData(ByteBuffer data) {
44 | try {
45 | final byte[] input = data.array();
46 | FileOutputStream fos = new FileOutputStream(file);
47 |
48 | final Deflater deflater = new Deflater(Deflater.BEST_SPEED, true);
49 | deflater.setInput(input, data.arrayOffset(), data.remaining());
50 | deflater.finish();
51 |
52 | byte[] buf = new byte[1024];
53 | while (!deflater.finished()) {
54 | int byteCount = deflater.deflate(buf);
55 | fos.write(buf, 0, byteCount);
56 | }
57 | deflater.end();
58 |
59 | fos.close();
60 | } catch (Exception e) {
61 | FileLog.e(e);
62 | }
63 | }
64 |
65 | public ByteBuffer getData() {
66 | try {
67 | byte[] input = new byte[1024];
68 | byte[] output = new byte[1024];
69 | FileInputStream fin = new FileInputStream(file);
70 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
71 | Inflater inflater = new Inflater(true);
72 |
73 | while (true) {
74 | int numRead = fin.read(input);
75 | if (numRead != -1) {
76 | inflater.setInput(input, 0, numRead);
77 | }
78 |
79 | int numDecompressed;
80 | while ((numDecompressed = inflater.inflate(output, 0, output.length)) != 0) {
81 | bos.write(output, 0, numDecompressed);
82 | }
83 |
84 | if (inflater.finished()) {
85 | break;
86 | }
87 | else if (inflater.needsInput()) {
88 | continue;
89 | }
90 | }
91 |
92 | inflater.end();
93 | ByteBuffer result = ByteBuffer.wrap(bos.toByteArray(), 0, bos.size());
94 |
95 | bos.close();
96 | fin.close();
97 |
98 | return result;
99 | } catch (Exception e) {
100 | FileLog.e(e);
101 | }
102 |
103 | return null;
104 | }
105 |
106 | public int getX() {
107 | return (int) bounds.left;
108 | }
109 |
110 | public int getY() {
111 | return (int) bounds.top;
112 | }
113 |
114 | public int getWidth() {
115 | return (int) bounds.width();
116 | }
117 |
118 | public int getHeight() {
119 | return (int) bounds.height();
120 | }
121 |
122 | public RectF getBounds() {
123 | return new RectF(bounds);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Swatch.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | public class Swatch {
4 |
5 | public int color;
6 | public float colorLocation;
7 | public float brushWeight;
8 |
9 | public Swatch(int color, float colorLocation, float brushWeight) {
10 | this.color = color;
11 | this.colorLocation = colorLocation;
12 | this.brushWeight = brushWeight;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Texture.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.Bitmap;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLUtils;
6 | import com.example.photopaint.views.components.Size;
7 |
8 | public class Texture {
9 |
10 | private Bitmap bitmap;
11 | private int texture;
12 |
13 | public Texture(Bitmap bitmap) {
14 | this.bitmap = bitmap;
15 | }
16 |
17 | public void cleanResources(boolean recycleBitmap) {
18 | if (texture == 0) {
19 | return;
20 | }
21 |
22 | int[] textures = new int[]{texture};
23 | GLES20.glDeleteTextures(1, textures, 0);
24 | texture = 0;
25 |
26 | if (recycleBitmap) {
27 | bitmap.recycle();
28 | }
29 | }
30 |
31 | private boolean isPOT(int x) {
32 | return (x & (x - 1)) == 0;
33 | }
34 |
35 | public int texture() {
36 | if (texture != 0) {
37 | return texture;
38 | }
39 |
40 | if (bitmap.isRecycled()) {
41 | return 0;
42 | }
43 |
44 | int[] textures = new int[1];
45 | GLES20.glGenTextures(1, textures, 0);
46 | texture = textures[0];
47 |
48 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
49 |
50 | boolean mipMappable = false; //isPOT(bitmap.getWidth()) && isPOT(bitmap.getHeight());
51 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
52 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
53 |
54 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
55 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, mipMappable ? GLES20.GL_LINEAR_MIPMAP_LINEAR : GLES20.GL_LINEAR);
56 |
57 | GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
58 |
59 | if (mipMappable) {
60 | GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
61 | }
62 |
63 | Utils.HasGLError();
64 |
65 | return texture;
66 | }
67 |
68 | public static int generateTexture(Size size) {
69 | int texture;
70 | int[] textures = new int[1];
71 | GLES20.glGenTextures(1, textures, 0);
72 | texture = textures[0];
73 |
74 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
75 |
76 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
77 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
78 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
79 | GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
80 |
81 | int width = (int) size.width;
82 | int height = (int) size.height;
83 | int format = GLES20.GL_RGBA;
84 | int type = GLES20.GL_UNSIGNED_BYTE;
85 |
86 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, format, width, height, 0, format, type, null);
87 |
88 | return texture;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/UndoStore.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import com.example.photopaint.helpers.AndroidUtilities;
4 |
5 | import java.util.*;
6 |
7 | public class UndoStore {
8 |
9 | public interface UndoStoreDelegate {
10 | void historyChanged();
11 | }
12 |
13 | private UndoStoreDelegate delegate;
14 | private Map uuidToOperationMap = new HashMap<>();
15 | private List operations = new ArrayList<>();
16 | private Map uuidRecoverOperateMap = new HashMap<>();
17 | private List recoverOperations = new Stack<>();
18 |
19 | public boolean canUndo() {
20 | return !operations.isEmpty();
21 | }
22 |
23 | public boolean canRecover(){
24 | return !recoverOperations.isEmpty();
25 | }
26 |
27 | public void setDelegate(UndoStoreDelegate undoStoreDelegate) {
28 | delegate = undoStoreDelegate;
29 | }
30 |
31 | public void registerUndo(UUID uuid, Runnable undoRunnable) {
32 | uuidToOperationMap.put(uuid, undoRunnable);
33 | operations.add(uuid);
34 |
35 | while (!recoverOperations.isEmpty()){
36 | int lastIndex = recoverOperations.size() - 1;
37 | UUID deleteId = recoverOperations.get(lastIndex);
38 |
39 | operations.remove(deleteId);
40 | uuidToOperationMap.remove(deleteId);
41 |
42 | recoverOperations.remove(lastIndex);
43 | uuidRecoverOperateMap.remove(deleteId);
44 | }
45 |
46 | notifyOfHistoryChanges();
47 | }
48 |
49 | public void unregisterUndo(UUID uuid) {
50 | uuidToOperationMap.remove(uuid);
51 | operations.remove(uuid);
52 |
53 | notifyOfHistoryChanges();
54 | }
55 |
56 | public void registerRecover(UUID uuid, Runnable recoverRunnable){
57 | uuidRecoverOperateMap.put(uuid, recoverRunnable);
58 | }
59 |
60 | public void unRegisterRecover(UUID uuid){
61 | uuidRecoverOperateMap.remove(uuid);
62 | }
63 |
64 | public void undo() {
65 | if (operations.size() == 0) {
66 | return;
67 | }
68 |
69 | int lastIndex = operations.size() - 1;
70 | UUID uuid = operations.get(lastIndex);
71 | Runnable undoRunnable = uuidToOperationMap.get(uuid);
72 |
73 | operations.remove(lastIndex);
74 | recoverOperations.add(uuid);
75 |
76 | undoRunnable.run();
77 | notifyOfHistoryChanges();
78 | }
79 |
80 | public void recover() {
81 | if (!canRecover()) {
82 | return;
83 | }
84 |
85 | int lastIndex = recoverOperations.size() - 1;
86 | UUID uuid = recoverOperations.get(lastIndex);
87 | recoverOperations.remove(lastIndex);
88 | Runnable recoverRunnable = uuidRecoverOperateMap.get(uuid);
89 |
90 | operations.add(uuid);
91 |
92 | recoverRunnable.run();
93 | notifyOfHistoryChanges();
94 | }
95 |
96 | public void reset() {
97 | // 清掉图层并清空撤销栈
98 | for (int index = operations.size() - 1; index >=0; index--){
99 | UUID uuid = operations.get(index);
100 | Runnable undoRunnable = uuidToOperationMap.get(uuid);
101 | uuidToOperationMap.remove(uuid);
102 | operations.remove(index);
103 |
104 | undoRunnable.run();
105 | }
106 |
107 | notifyOfHistoryChanges();
108 | }
109 |
110 | private void notifyOfHistoryChanges() {
111 | AndroidUtilities.runOnUIThread(new Runnable() {
112 | @Override
113 | public void run() {
114 | if (delegate != null) {
115 | delegate.historyChanged();
116 | }
117 | }
118 | });
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/Utils.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint;
2 |
3 | import android.graphics.RectF;
4 | import android.opengl.GLES20;
5 | import android.opengl.GLUtils;
6 | import android.util.Log;
7 |
8 | public class Utils {
9 |
10 | public static void HasGLError() {
11 | int error = GLES20.glGetError();
12 | if (error != 0) {
13 | Log.d("Paint", GLUtils.getEGLErrorString(error));
14 | }
15 | }
16 |
17 | public static void RectFIntegral(RectF rect) {
18 | rect.left = (int) Math.floor(rect.left);
19 | rect.top = (int) Math.floor(rect.top);
20 | rect.right = (int) Math.ceil(rect.right);
21 | rect.bottom = (int) Math.ceil(rect.bottom);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/views/EditTextOutline.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.*;
5 | import android.text.Layout;
6 | import android.text.StaticLayout;
7 | import android.text.TextPaint;
8 | import android.widget.EditText;
9 |
10 | public class EditTextOutline extends EditText {
11 |
12 | private final Canvas mCanvas = new Canvas();
13 | private final TextPaint mPaint = new TextPaint();
14 | private Bitmap mCache;
15 | private boolean mUpdateCachedBitmap;
16 | private int mStrokeColor;
17 | private float mStrokeWidth;
18 | private boolean isEditing;
19 |
20 | public EditTextOutline(Context context) {
21 | super(context);
22 |
23 | mStrokeColor = Color.TRANSPARENT;
24 |
25 | mUpdateCachedBitmap = true;
26 | mPaint.setAntiAlias(true);
27 | mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
28 | }
29 |
30 | protected void onTextChanged(CharSequence text, int start, int before, int after) {
31 | super.onTextChanged(text, start, before, after);
32 | mUpdateCachedBitmap = true;
33 | }
34 |
35 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
36 | super.onSizeChanged(w, h, oldw, oldh);
37 | if (w > 0 && h > 0) {
38 | mUpdateCachedBitmap = true;
39 | mCache = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
40 | } else {
41 | mCache = null;
42 | }
43 | }
44 |
45 | public void setStrokeColor(int strokeColor) {
46 | this.mStrokeColor = strokeColor;
47 | mUpdateCachedBitmap = true;
48 | invalidate();
49 | }
50 |
51 | public void setStrokeWidth(float strokeWidth) {
52 | this.mStrokeWidth = strokeWidth;
53 | mUpdateCachedBitmap = true;
54 | invalidate();
55 | }
56 |
57 | public void setEditing(boolean isEditing){
58 | this.isEditing = isEditing;
59 | invalidate();
60 | }
61 |
62 | protected void onDraw(Canvas canvas) {
63 | if (!isEditing && mCache != null && mStrokeColor != Color.TRANSPARENT) {
64 | if (mUpdateCachedBitmap) {
65 | final int w = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
66 | final int h = getMeasuredHeight();
67 | final String text = getText().toString();
68 |
69 | mCanvas.setBitmap(mCache);
70 | mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
71 |
72 | float strokeWidth = mStrokeWidth > 0 ? mStrokeWidth : (float)Math.ceil(getTextSize() / 11.5f);
73 | mPaint.setStrokeWidth(strokeWidth);
74 | mPaint.setColor(Color.WHITE);
75 | mPaint.setTextSize(getTextSize());
76 | mPaint.setTypeface(getTypeface());
77 | mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
78 |
79 | StaticLayout sl = new StaticLayout(text, mPaint, w, Layout.Alignment.ALIGN_CENTER, 1, 0, true);
80 |
81 | mCanvas.save();
82 | float a = (h - getPaddingTop() - getPaddingBottom() - sl.getHeight()) / 2.0f;
83 | mCanvas.translate(getPaddingLeft(), a + getPaddingTop());
84 | sl.draw(mCanvas);
85 | mCanvas.restore();
86 |
87 | mUpdateCachedBitmap = false;
88 | }
89 | canvas.drawBitmap(mCache, 0, 0, mPaint);
90 | }
91 | setTextColor(isEditing ? Color.WHITE : mStrokeColor);
92 | super.onDraw(canvas);
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/views/EntitiesContainerView.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint.views;
2 |
3 | import android.content.Context;
4 | import android.view.MotionEvent;
5 | import android.view.ScaleGestureDetector;
6 | import android.view.View;
7 | import android.widget.FrameLayout;
8 |
9 | public class EntitiesContainerView extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener, RotationGestureDetector.OnRotationGestureListener {
10 |
11 | public interface EntitiesContainerViewDelegate {
12 | boolean shouldReceiveTouches();
13 | void onEntityDeselect();
14 | EntityView onSelectedEntityRequest();
15 | }
16 |
17 | private EntitiesContainerViewDelegate delegate;
18 | private ScaleGestureDetector gestureDetector;
19 | private RotationGestureDetector rotationGestureDetector;
20 | private float previousScale = 1.0f;
21 | private float previousAngle;
22 | private boolean hasTransformed;
23 |
24 | public EntitiesContainerView(Context context, EntitiesContainerViewDelegate entitiesContainerViewDelegate) {
25 | super(context);
26 |
27 | gestureDetector = new ScaleGestureDetector(context, this);
28 | rotationGestureDetector = new RotationGestureDetector(this);
29 | delegate = entitiesContainerViewDelegate;
30 | }
31 |
32 | public int entitiesCount() {
33 | int count = 0;
34 | for (int index = 0; index < getChildCount(); index++) {
35 | View view = getChildAt(index);
36 | if (!(view instanceof EntityView)) {
37 | continue;
38 | }
39 | count++;
40 | }
41 | return count;
42 | }
43 |
44 | public void bringViewToFront(EntityView view) {
45 | if (indexOfChild(view) != getChildCount() - 1) {
46 | removeView(view);
47 | addView(view, getChildCount());
48 | }
49 | }
50 |
51 | @Override
52 | public boolean onInterceptTouchEvent(MotionEvent ev) {
53 | return ev.getPointerCount() == 2 && delegate.shouldReceiveTouches();
54 | }
55 |
56 | @Override
57 | public boolean onTouchEvent(MotionEvent event) {
58 | EntityView selectedEntity = delegate.onSelectedEntityRequest();
59 | if (selectedEntity == null) {
60 | return false;
61 | }
62 |
63 | if (event.getPointerCount() == 1)
64 | {
65 | int action = event.getActionMasked();
66 | if (action == MotionEvent.ACTION_DOWN) {
67 | hasTransformed = false;
68 | }
69 | else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_MOVE) {
70 | if (!hasTransformed && delegate != null) {
71 | delegate.onEntityDeselect();
72 | }
73 | return false;
74 | }
75 | }
76 |
77 | gestureDetector.onTouchEvent(event);
78 | rotationGestureDetector.onTouchEvent(event);
79 | return true;
80 | }
81 |
82 | @Override
83 | public boolean onScale(ScaleGestureDetector detector) {
84 | float sf = detector.getScaleFactor();
85 | float newScale = sf / previousScale;
86 |
87 | EntityView view = delegate.onSelectedEntityRequest();
88 | view.scale(newScale);
89 |
90 | previousScale = sf;
91 |
92 | return false;
93 | }
94 |
95 | @Override
96 | public boolean onScaleBegin(ScaleGestureDetector detector) {
97 | previousScale = 1.0f;
98 | hasTransformed = true;
99 | return true;
100 | }
101 |
102 | @Override
103 | public void onScaleEnd(ScaleGestureDetector detector) {
104 |
105 | }
106 |
107 | @Override
108 | public void onRotationBegin(RotationGestureDetector rotationDetector) {
109 | previousAngle = rotationDetector.getStartAngle();
110 | hasTransformed = true;
111 | }
112 |
113 | @Override
114 | public void onRotation(RotationGestureDetector rotationDetector) {
115 | EntityView view = delegate.onSelectedEntityRequest();
116 | float angle = rotationDetector.getAngle();
117 | float delta = previousAngle - angle;
118 | view.rotate(view.getRotation() + delta);
119 | previousAngle = angle;
120 | }
121 |
122 | @Override
123 | public void onRotationEnd(RotationGestureDetector rotationDetector) {
124 |
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/views/RotationGestureDetector.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint.views;
2 |
3 | import android.view.MotionEvent;
4 |
5 | public class RotationGestureDetector {
6 |
7 | public interface OnRotationGestureListener {
8 | void onRotationBegin(RotationGestureDetector rotationDetector);
9 | void onRotation(RotationGestureDetector rotationDetector);
10 | void onRotationEnd(RotationGestureDetector rotationDetector);
11 | }
12 |
13 | private float fX, fY, sX, sY;
14 | private float angle;
15 | private float startAngle;
16 |
17 | private OnRotationGestureListener mListener;
18 |
19 | public float getAngle() {
20 | return angle;
21 | }
22 |
23 | public float getStartAngle() {
24 | return startAngle;
25 | }
26 |
27 | public RotationGestureDetector(OnRotationGestureListener listener) {
28 | mListener = listener;
29 | }
30 |
31 | public boolean onTouchEvent(MotionEvent event) {
32 | if (event.getPointerCount() != 2)
33 | return false;
34 |
35 | switch (event.getActionMasked()) {
36 | case MotionEvent.ACTION_DOWN:
37 | case MotionEvent.ACTION_POINTER_DOWN: {
38 | sX = event.getX(0);
39 | sY = event.getY(0);
40 | fX = event.getX(1);
41 | fY = event.getY(1);
42 | }
43 | break;
44 |
45 | case MotionEvent.ACTION_MOVE: {
46 | float nfX, nfY, nsX, nsY;
47 | nsX = event.getX(0);
48 | nsY = event.getY(0);
49 | nfX = event.getX(1);
50 | nfY = event.getY(1);
51 |
52 | angle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);
53 |
54 | if (mListener != null) {
55 | if (Float.isNaN(startAngle)) {
56 | startAngle = angle;
57 | mListener.onRotationBegin(this);
58 | } else {
59 | mListener.onRotation(this);
60 | }
61 | }
62 | }
63 | break;
64 |
65 | case MotionEvent.ACTION_UP:
66 | case MotionEvent.ACTION_CANCEL: {
67 | startAngle = Float.NaN;
68 | }
69 | break;
70 |
71 | case MotionEvent.ACTION_POINTER_UP: {
72 | startAngle = Float.NaN;
73 |
74 | if (mListener != null) {
75 | mListener.onRotationEnd(this);
76 | }
77 | }
78 | break;
79 | }
80 | return true;
81 | }
82 |
83 | private float angleBetweenLines(float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY) {
84 | float angle1 = (float) Math.atan2((fY - sY), (fX - sX));
85 | float angle2 = (float) Math.atan2((nfY - nsY), (nfX - nsX));
86 | float angle = ((float) Math.toDegrees(angle1 - angle2)) % 360;
87 | if (angle < -180.f) {
88 | angle += 360.0f;
89 | }
90 | if (angle > 180.f) {
91 | angle -= 360.0f;
92 | }
93 | return angle;
94 | }
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/views/StickerView.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.RectF;
9 | import android.view.ViewGroup;
10 | import android.widget.FrameLayout;
11 |
12 | import com.example.photopaint.helpers.AndroidUtilities;
13 | //import org.telegram.messenger.FileLoader;
14 | //import org.telegram.messenger.ImageLocation;
15 | //import org.telegram.messenger.ImageReceiver;
16 | import com.example.photopaint.R;
17 | import com.example.photopaint.views.components.LayoutHelper;
18 | import com.example.photopaint.views.components.Point;
19 | import com.example.photopaint.views.components.Rect;
20 | import com.example.photopaint.views.components.Size;
21 |
22 | public class StickerView extends EntityView {
23 |
24 | private class FrameLayoutDrawer extends FrameLayout {
25 | public FrameLayoutDrawer(Context context) {
26 | super(context);
27 | setWillNotDraw(false);
28 | }
29 |
30 | @Override
31 | protected void onDraw(Canvas canvas) {
32 | StickerView.this.stickerDraw(canvas);
33 | }
34 | }
35 |
36 | private Bitmap sticker;
37 | private Object parentObject;
38 | private int anchor = -1;
39 | private boolean mirrored = false;
40 | private Size baseSize;
41 |
42 | private FrameLayoutDrawer containerView;
43 | // private ImageReceiver centerImage = new ImageReceiver();
44 |
45 | public StickerView(Context context, Point position, Size baseSize, Bitmap sticker, Object parentObject) {
46 | this(context, position, 0.0f, 1.0f, baseSize, sticker, parentObject);
47 | }
48 |
49 | public StickerView(Context context, Point position, float angle, float scale, Size baseSize, Bitmap sticker, Object parentObject) {
50 | super(context, position);
51 | setRotation(angle);
52 | setScale(scale);
53 |
54 | this.sticker = sticker;
55 | this.baseSize = baseSize;
56 | this.parentObject = parentObject;
57 |
58 | // for (int a = 0; a < sticker.attributes.size(); a++) {
59 | // TLRPC.DocumentAttribute attribute = sticker.attributes.get(a);
60 | // if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
61 | // if (attribute.mask_coords != null)
62 | // anchor = attribute.mask_coords.n;
63 | // break;
64 | // }
65 | // }
66 |
67 | containerView = new FrameLayoutDrawer(context);
68 | addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
69 |
70 | // centerImage.setAspectFit(true);
71 | // centerImage.setInvalidateAll(true);
72 | // centerImage.setParentView(containerView);
73 | // TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(sticker.thumbs, 90);
74 | // centerImage.setImage(ImageLocation.getForDocument(sticker), null, ImageLocation.getForDocument(thumb, sticker), null, "webp", parentObject, 1);
75 |
76 | updatePosition();
77 | }
78 |
79 | public StickerView(Context context, StickerView stickerView, Point position) {
80 | this(context, position, stickerView.getRotation(), stickerView.getScale(), stickerView.baseSize, stickerView.sticker, stickerView.parentObject);
81 | if (stickerView.mirrored) {
82 | mirror();
83 | }
84 | }
85 |
86 | public int getAnchor() {
87 | return anchor;
88 | }
89 |
90 | public void mirror() {
91 | mirrored = !mirrored;
92 | containerView.invalidate();
93 | }
94 |
95 | protected void updatePosition() {
96 | float halfWidth = baseSize.width / 2.0f;
97 | float halfHeight = baseSize.height / 2.0f;
98 | setX(position.x - halfWidth);
99 | setY(position.y - halfHeight);
100 | updateSelectionView();
101 | }
102 |
103 | protected void stickerDraw(Canvas canvas) {
104 | if (containerView == null) {
105 | return;
106 | }
107 |
108 | canvas.save();
109 | // Bitmap bitmap = centerImage.getBitmap();
110 | Bitmap stickerImage = BitmapFactory.decodeResource(getResources(), R.drawable.sticker_demo);
111 | if (stickerImage != null) {
112 | if (mirrored) {
113 | canvas.scale(-1.0f, 1.0f);
114 | canvas.translate(-baseSize.width, 0);
115 | }
116 | // centerImage.setImageCoords(0, 0, (int) baseSize.width, (int) baseSize.height);
117 | // centerImage.draw(canvas);
118 |
119 | // float left = (baseSize.width - bitmap.getWidth())/2;
120 | // float top = (baseSize.height - bitmap.getHeight()) / 2;
121 | // canvas.drawBitmap(bitmap,left,top, new Paint());
122 |
123 | float stickImageW = stickerImage.getWidth() ;
124 | float stickImageH = stickerImage.getHeight();
125 | // float scaleW = stickImageW == 0 ? 1f : baseSize.width / stickImageW;
126 | // float scaleH = stickImageH == 0 ? 1f : baseSize.height /stickImageH;
127 | // float scale = Math.min(scaleW, scaleH);
128 | //
129 | // stickImageW *= scale;
130 | // stickImageH *= scale;
131 | canvas.drawBitmap(stickerImage,(baseSize.width - stickImageW) / 2 ,(baseSize.height - stickImageH) / 2, new Paint());
132 | }
133 | canvas.restore();
134 | }
135 |
136 | @Override
137 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
138 | super.onMeasure(MeasureSpec.makeMeasureSpec((int) baseSize.width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec((int) baseSize.height, MeasureSpec.EXACTLY));
139 | }
140 |
141 | @Override
142 | protected Rect getSelectionBounds() {
143 | ViewGroup parentView = (ViewGroup) getParent();
144 | float scale = parentView.getScaleX();
145 |
146 | float side = getWidth() * (getScale() + 0.4f);
147 | return new Rect((position.x - side / 2.0f) * scale, (position.y - side / 2.0f) * scale, side * scale, side * scale);
148 | }
149 |
150 | @Override
151 | protected SelectionView createSelectionView() {
152 | return new StickerViewSelectionView(getContext());
153 | }
154 |
155 | public Bitmap getSticker() {
156 | return sticker;
157 | }
158 |
159 | public class StickerViewSelectionView extends SelectionView {
160 |
161 | private Paint arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
162 | private RectF arcRect = new RectF();
163 |
164 | public StickerViewSelectionView(Context context) {
165 | super(context);
166 |
167 | arcPaint.setColor(0xffffffff);
168 | arcPaint.setStrokeWidth(AndroidUtilities.dp(1));
169 | arcPaint.setStyle(Paint.Style.STROKE);
170 | }
171 |
172 | @Override
173 | protected int pointInsideHandle(float x, float y) {
174 | float thickness = AndroidUtilities.dp(1.0f);
175 | float radius = AndroidUtilities.dp(19.5f);
176 |
177 | float inset = radius + thickness;
178 | float middle = inset + (getHeight() - inset * 2) / 2.0f;
179 |
180 | if (x > inset - radius && y > middle - radius && x < inset + radius && y < middle + radius) {
181 | return SELECTION_LEFT_HANDLE;
182 | } else if (x > inset + (getWidth() - inset * 2) - radius && y > middle - radius && x < inset + (getWidth() - inset * 2) + radius && y < middle + radius) {
183 | return SELECTION_RIGHT_HANDLE;
184 | }
185 |
186 | float selectionRadius = getWidth() / 2.0f;
187 |
188 | if (Math.pow(x - selectionRadius, 2) + Math.pow(y - selectionRadius, 2) < Math.pow(selectionRadius, 2)) {
189 | return SELECTION_WHOLE_HANDLE;
190 | }
191 |
192 | return 0;
193 | }
194 |
195 | @Override
196 | protected void onDraw(Canvas canvas) {
197 | super.onDraw(canvas);
198 |
199 | float thickness = AndroidUtilities.dp(1.0f);
200 | float radius = AndroidUtilities.dp(4.5f);
201 |
202 | float inset = radius + thickness + AndroidUtilities.dp(15);
203 | float mainRadius = getWidth() / 2 - inset;
204 |
205 | float space = 4.0f;
206 | float length = 4.0f;
207 |
208 | arcRect.set(inset, inset, inset + mainRadius * 2, inset + mainRadius * 2);
209 | for (int i = 0; i < 48; i++) {
210 | canvas.drawArc(arcRect, i * (space + length), length, false, arcPaint);
211 | }
212 |
213 | canvas.drawCircle(inset, inset + mainRadius, radius, dotPaint);
214 | canvas.drawCircle(inset, inset + mainRadius, radius, dotStrokePaint);
215 |
216 | canvas.drawCircle(inset + mainRadius * 2, inset + mainRadius, radius, dotPaint);
217 | canvas.drawCircle(inset + mainRadius * 2, inset + mainRadius, radius, dotStrokePaint);
218 | }
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/photopaint/views/components/paint/views/TextPaintView.java:
--------------------------------------------------------------------------------
1 | package com.example.photopaint.views.components.paint.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Typeface;
7 | import android.os.Build;
8 | import android.text.Editable;
9 | import android.text.Layout;
10 | import android.text.TextWatcher;
11 | import android.util.TypedValue;
12 | import android.view.Gravity;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.view.inputmethod.EditorInfo;
16 | import android.view.inputmethod.InputMethodManager;
17 | import com.example.photopaint.helpers.AndroidUtilities;
18 | import com.example.photopaint.views.components.LayoutHelper;
19 | import com.example.photopaint.views.components.paint.Swatch;
20 | import com.example.photopaint.views.components.Point;
21 | import com.example.photopaint.views.components.Rect;
22 |
23 | public class TextPaintView extends EntityView {
24 |
25 | private EditTextOutline editText;
26 | private Swatch swatch;
27 | private boolean stroke;
28 | private int baseFontSize;
29 |
30 | public TextPaintView(Context context, Point position, int fontSize, String text, Swatch swatch, boolean stroke) {
31 | super(context, position);
32 |
33 | baseFontSize = fontSize;
34 |
35 | editText = new EditTextOutline(context);
36 | editText.setBackgroundColor(Color.TRANSPARENT);
37 | editText.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7));
38 | editText.setClickable(false);
39 | editText.setEnabled(false);
40 | editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, baseFontSize);
41 | editText.setText(text);
42 | editText.setTextColor(swatch.color);
43 | editText.setTypeface(null, Typeface.BOLD);
44 | editText.setGravity(Gravity.CENTER);
45 | editText.setHorizontallyScrolling(false);
46 | editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
47 | editText.setFocusableInTouchMode(true);
48 | editText.setInputType(editText.getInputType() | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES);
49 | addView(editText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
50 |
51 | if (Build.VERSION.SDK_INT >= 23) {
52 | editText.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
53 | }
54 |
55 | setSwatch(swatch);
56 | setStroke(stroke);
57 |
58 | updatePosition();
59 |
60 | editText.addTextChangedListener(new TextWatcher() {
61 | private String text;
62 | private int beforeCursorPosition = 0;
63 |
64 | @Override
65 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
66 | text = s.toString();
67 | beforeCursorPosition = start;
68 | }
69 |
70 | @Override
71 | public void onTextChanged(CharSequence s, int start, int before, int count) {
72 |
73 | }
74 |
75 | @Override
76 | public void afterTextChanged(Editable s) {
77 | editText.removeTextChangedListener(this);
78 |
79 | if (editText.getLineCount() > 9) {
80 | editText.setText(text);
81 | editText.setSelection(beforeCursorPosition);
82 | }
83 |
84 | editText.addTextChangedListener(this);
85 | }
86 | });
87 | }
88 |
89 | public TextPaintView(Context context, TextPaintView textPaintView, Point position) {
90 | this(context, position, textPaintView.baseFontSize, textPaintView.getText(), textPaintView.getSwatch(), textPaintView.stroke);
91 | setRotation(textPaintView.getRotation());
92 | setScale(textPaintView.getScale());
93 | }
94 |
95 | public void setMaxWidth(int maxWidth) {
96 | editText.setMaxWidth(maxWidth);
97 | }
98 |
99 | @Override
100 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
101 | super.onLayout(changed, left, top, right, bottom);
102 | updatePosition();
103 | }
104 |
105 | public String getText() {
106 | return editText.getText().toString();
107 | }
108 |
109 | public void setText(String text) {
110 | editText.setText(text);
111 | }
112 |
113 | public View getFocusedView() {
114 | return editText;
115 | }
116 |
117 | public void beginEditing() {
118 | editText.setEnabled(true);
119 | editText.setClickable(true);
120 | editText.requestFocus();
121 | editText.setSelection(editText.getText().length());
122 | editText.setEditing(true);
123 | showKeyboard();
124 | }
125 |
126 | public void endEditing() {
127 | editText.clearFocus();
128 | editText.setEnabled(false);
129 | editText.setClickable(false);
130 | updateSelectionView();
131 | editText.setEditing(false);
132 | hideKeyboard();
133 | }
134 |
135 | private boolean showKeyboard() {
136 | try {
137 | InputMethodManager inputManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
138 | return inputManager.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
139 | } catch (Exception e) {
140 | // FileLog.e(e);
141 | }
142 | return false;
143 | }
144 |
145 | public void hideKeyboard() {
146 | try {
147 | InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
148 | if (!imm.isActive()) {
149 | return;
150 | }
151 | imm.hideSoftInputFromWindow(getWindowToken(), 0);
152 | } catch (Exception e) {
153 | // FileLog.e(e);
154 | }
155 | }
156 |
157 | public Swatch getSwatch() {
158 | return swatch;
159 | }
160 |
161 | public void setSwatch(Swatch swatch) {
162 | this.swatch = swatch;
163 | updateColor();
164 | }
165 |
166 | public void setStroke(boolean stroke) {
167 | this.stroke = stroke;
168 | updateColor();
169 | }
170 |
171 | private void updateColor() {
172 | if (stroke) {
173 | editText.setTextColor(0xffffffff);
174 | editText.setStrokeColor(swatch.color);
175 | editText.setShadowLayer(0, 0, 0, 0);
176 | } else {
177 | editText.setTextColor(swatch.color);
178 | editText.setStrokeColor(Color.TRANSPARENT);
179 | editText.setShadowLayer(8, 0, 2, 0xaa000000);
180 | }
181 | }
182 |
183 | @Override
184 | protected Rect getSelectionBounds() {
185 | ViewGroup parentView = (ViewGroup) getParent();
186 | float scale = parentView.getScaleX();
187 | float width = getWidth() * (getScale()) + AndroidUtilities.dp(46) / scale;
188 | float height = getHeight() * (getScale()) + AndroidUtilities.dp(20) / scale;
189 | return new Rect((position.x - width / 2.0f) * scale, (position.y - height / 2.0f) * scale, width * scale, height * scale);
190 | }
191 |
192 | protected TextViewSelectionView createSelectionView() {
193 | return new TextViewSelectionView(getContext());
194 | }
195 |
196 | public class TextViewSelectionView extends SelectionView {
197 |
198 | public TextViewSelectionView(Context context) {
199 | super(context);
200 | }
201 |
202 | @Override
203 | protected int pointInsideHandle(float x, float y) {
204 | float thickness = AndroidUtilities.dp(1.0f);
205 | float radius = AndroidUtilities.dp(19.5f);
206 |
207 | float inset = radius + thickness;
208 | float width = getWidth() - inset * 2;
209 | float height = getHeight() - inset * 2;
210 |
211 | float middle = inset + height / 2.0f;
212 |
213 | if (x > inset - radius && y > middle - radius && x < inset + radius && y < middle + radius) {
214 | return SELECTION_LEFT_HANDLE;
215 | } else if (x > inset + width - radius && y > middle - radius && x < inset + width + radius && y < middle + radius) {
216 | return SELECTION_RIGHT_HANDLE;
217 | }
218 |
219 | if (x > inset && x < width && y > inset && y < height) {
220 | return SELECTION_WHOLE_HANDLE;
221 | }
222 |
223 | return 0;
224 | }
225 |
226 | @Override
227 | protected void onDraw(Canvas canvas) {
228 | super.onDraw(canvas);
229 |
230 | float space = AndroidUtilities.dp(3.0f);
231 | float length = AndroidUtilities.dp(3.0f);
232 | float thickness = AndroidUtilities.dp(1.0f);
233 | float radius = AndroidUtilities.dp(4.5f);
234 |
235 | float inset = radius + thickness + AndroidUtilities.dp(15);
236 |
237 | float width = getWidth() - inset * 2;
238 | float height = getHeight() - inset * 2;
239 |
240 | int xCount = (int) (Math.floor(width / (space + length)));
241 | float xGap = (float) Math.ceil(((width - xCount * (space + length)) + space) / 2.0f);
242 |
243 | for (int i = 0; i < xCount; i++) {
244 | float x = xGap + inset + i * (length + space);
245 | canvas.drawRect(x, inset - thickness / 2.0f, x + length, inset + thickness / 2.0f, paint);
246 | canvas.drawRect(x, inset + height - thickness / 2.0f, x + length, inset + height + thickness / 2.0f, paint);
247 | }
248 |
249 | int yCount = (int) (Math.floor(height / (space + length)));
250 | float yGap = (float) Math.ceil(((height - yCount * (space + length)) + space) / 2.0f);
251 |
252 | for (int i = 0; i < yCount; i++) {
253 | float y = yGap + inset + i * (length + space);
254 | canvas.drawRect(inset - thickness / 2.0f, y, inset + thickness / 2.0f, y + length, paint);
255 | canvas.drawRect(inset + width - thickness / 2.0f, y, inset + width + thickness / 2.0f, y + length, paint);
256 | }
257 |
258 | canvas.drawCircle(inset, inset + height / 2.0f, radius, dotPaint);
259 | canvas.drawCircle(inset, inset + height / 2.0f, radius, dotStrokePaint);
260 |
261 | canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotPaint);
262 | canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotStrokePaint);
263 | }
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popup_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popup_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/header_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/header_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/header_shadow_reverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/header_shadow_reverse.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_ab_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/ic_ab_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/knob_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/knob_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_blue_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_blue_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_green_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_green_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_red_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_red_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_yellow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/media_doc_yellow_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/media_doc_yellow_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_flip.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_mosaic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_mosaic.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_paint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_paint.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_paint_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_paint_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_paint_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_paint_text.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_recover.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_sticker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_sticker.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/photo_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/photo_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/popup_fixed_alert.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/popup_fixed_alert.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/popup_fixed_alert2.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/popup_fixed_alert2.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/popup_fixed_top.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-hdpi/popup_fixed_top.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/header_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/header_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/header_shadow_reverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/header_shadow_reverse.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_ab_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/ic_ab_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/knob_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/knob_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_blue_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_blue_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_green_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_green_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_red_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_red_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_yellow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/media_doc_yellow_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/media_doc_yellow_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_flip.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_mosaic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_mosaic.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_paint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_paint.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_paint_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_paint_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_paint_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_paint_text.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_recover.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_sticker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_sticker.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/photo_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/photo_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/popup_fixed_alert.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/popup_fixed_alert.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/popup_fixed_alert2.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/popup_fixed_alert2.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/popup_fixed_top.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-mdpi/popup_fixed_top.9.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/header_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/header_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/header_shadow_reverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/header_shadow_reverse.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_ab_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/ic_ab_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/knob_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/knob_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_blue_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_blue_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_green_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_green_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_red_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_red_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_yellow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/media_doc_yellow_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/media_doc_yellow_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_flip.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_mosaic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_mosaic.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_paint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_paint.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_paint_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_paint_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_paint_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_paint_text.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_recover.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_sticker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_sticker.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/photo_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/photo_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/popup_fixed_alert.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/popup_fixed_alert.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/popup_fixed_alert2.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/popup_fixed_alert2.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/popup_fixed_top.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xhdpi/popup_fixed_top.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/adv_img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/adv_img_2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/header_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/header_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/header_shadow_reverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/header_shadow_reverse.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_ab_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/ic_ab_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/knob_shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/knob_shadow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_blue_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_blue_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_green_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_green_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_load.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_load.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_red_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_red_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_yellow.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/media_doc_yellow_b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/media_doc_yellow_b.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_flip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_flip.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_mosaic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_mosaic.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_outline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_outline.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_paint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_paint.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_paint_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_paint_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_paint_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_paint_text.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_recover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_recover.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_sticker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_sticker.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/photo_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/photo_undo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/popup_fixed_alert.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/popup_fixed_alert.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/popup_fixed_alert2.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/popup_fixed_alert2.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/popup_fixed_top.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable-xxhdpi/popup_fixed_top.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/adv_img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/adv_img_2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/catstile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/catstile.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/field_carret_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/mosaic_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/mosaic_black.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/mosaic_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/mosaic_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_elliptical_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_elliptical_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_elliptical_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_elliptical_preview.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_neon_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_neon_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_neon_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_neon_preview.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_radial_brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_radial_brush.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint_radial_preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/paint_radial_preview.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/screenshot.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sticker_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/drawable/sticker_demo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/transparent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/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/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PhotoPaint
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
24 |
25 |
28 |
29 |
38 |
39 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/photopaint/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.photopaint
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.31'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.4.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
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 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gifs/1571733722289.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gifs/1571733722289.gif
--------------------------------------------------------------------------------
/gifs/1571733767440.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gifs/1571733767440.gif
--------------------------------------------------------------------------------
/gifs/1571733806801.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gifs/1571733806801.gif
--------------------------------------------------------------------------------
/gifs/1571733849769.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gifs/1571733849769.gif
--------------------------------------------------------------------------------
/gifs/1571733896635.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gifs/1571733896635.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # Kotlin code style for this project: "official" or "obsolete":
15 | kotlin.code.style=official
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rave-Tian/PhotoPaint/0a715c9845510183a73fefa8aa9af798e240706a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jul 15 17:35:18 CST 2019
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-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/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 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
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 Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------