Commit af6964e60d5f6f874247a7e8b6a26b738c30ec71
0 parents
create new git
Showing
329 changed files
with
6653 additions
and
0 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 329 files are displayed.
1 | +++ a/.idea/compiler.xml | |
... | ... | @@ -0,0 +1,22 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project version="4"> | |
3 | + <component name="CompilerConfiguration"> | |
4 | + <resourceExtensions /> | |
5 | + <wildcardResourcePatterns> | |
6 | + <entry name="!?*.java" /> | |
7 | + <entry name="!?*.form" /> | |
8 | + <entry name="!?*.class" /> | |
9 | + <entry name="!?*.groovy" /> | |
10 | + <entry name="!?*.scala" /> | |
11 | + <entry name="!?*.flex" /> | |
12 | + <entry name="!?*.kt" /> | |
13 | + <entry name="!?*.clj" /> | |
14 | + <entry name="!?*.aj" /> | |
15 | + </wildcardResourcePatterns> | |
16 | + <annotationProcessing> | |
17 | + <profile default="true" name="Default" enabled="false"> | |
18 | + <processorPath useClasspath="true" /> | |
19 | + </profile> | |
20 | + </annotationProcessing> | |
21 | + </component> | |
22 | +</project> | |
0 | 23 | \ No newline at end of file | ... | ... |
1 | +++ a/.idea/gradle.xml | |
... | ... | @@ -0,0 +1,18 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project version="4"> | |
3 | + <component name="GradleSettings"> | |
4 | + <option name="linkedExternalProjectsSettings"> | |
5 | + <GradleProjectSettings> | |
6 | + <option name="distributionType" value="DEFAULT_WRAPPED" /> | |
7 | + <option name="externalProjectPath" value="$PROJECT_DIR$" /> | |
8 | + <option name="modules"> | |
9 | + <set> | |
10 | + <option value="$PROJECT_DIR$" /> | |
11 | + <option value="$PROJECT_DIR$/app" /> | |
12 | + </set> | |
13 | + </option> | |
14 | + <option name="resolveModulePerSourceSet" value="false" /> | |
15 | + </GradleProjectSettings> | |
16 | + </option> | |
17 | + </component> | |
18 | +</project> | |
0 | 19 | \ No newline at end of file | ... | ... |
1 | +++ a/.idea/misc.xml | |
... | ... | @@ -0,0 +1,36 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project version="4"> | |
3 | + <component name="EntryPointsManager"> | |
4 | + <entry_points version="2.0" /> | |
5 | + </component> | |
6 | + <component name="NullableNotNullManager"> | |
7 | + <option name="myDefaultNullable" value="android.support.annotation.Nullable" /> | |
8 | + <option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> | |
9 | + <option name="myNullables"> | |
10 | + <value> | |
11 | + <list size="4"> | |
12 | + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> | |
13 | + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> | |
14 | + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> | |
15 | + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> | |
16 | + </list> | |
17 | + </value> | |
18 | + </option> | |
19 | + <option name="myNotNulls"> | |
20 | + <value> | |
21 | + <list size="4"> | |
22 | + <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> | |
23 | + <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> | |
24 | + <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> | |
25 | + <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> | |
26 | + </list> | |
27 | + </value> | |
28 | + </option> | |
29 | + </component> | |
30 | + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | |
31 | + <output url="file://$PROJECT_DIR$/build/classes" /> | |
32 | + </component> | |
33 | + <component name="ProjectType"> | |
34 | + <option name="id" value="Android" /> | |
35 | + </component> | |
36 | +</project> | |
0 | 37 | \ No newline at end of file | ... | ... |
1 | +++ a/.idea/modules.xml | |
... | ... | @@ -0,0 +1,9 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project version="4"> | |
3 | + <component name="ProjectModuleManager"> | |
4 | + <modules> | |
5 | + <module fileurl="file://$PROJECT_DIR$/AUTRE.iml" filepath="$PROJECT_DIR$/AUTRE.iml" /> | |
6 | + <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> | |
7 | + </modules> | |
8 | + </component> | |
9 | +</project> | |
0 | 10 | \ No newline at end of file | ... | ... |
1 | +++ a/.idea/runConfigurations.xml | |
... | ... | @@ -0,0 +1,12 @@ |
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<project version="4"> | |
3 | + <component name="RunConfigurationProducerService"> | |
4 | + <option name="ignoredProducers"> | |
5 | + <set> | |
6 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> | |
7 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> | |
8 | + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> | |
9 | + </set> | |
10 | + </option> | |
11 | + </component> | |
12 | +</project> | |
0 | 13 | \ No newline at end of file | ... | ... |
1 | +++ a/acrylic/build.gradle | |
... | ... | @@ -0,0 +1,32 @@ |
1 | +apply plugin: 'com.android.application' | |
2 | + | |
3 | +android { | |
4 | + compileSdkVersion 25 | |
5 | + buildToolsVersion "25.0.2" | |
6 | + | |
7 | + defaultConfig { | |
8 | + applicationId "anupam.acrylic" | |
9 | + minSdkVersion 15 | |
10 | + targetSdkVersion 25 | |
11 | + versionCode 1 | |
12 | + versionName "1.0" | |
13 | + | |
14 | + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | |
15 | + | |
16 | + } | |
17 | + buildTypes { | |
18 | + release { | |
19 | + minifyEnabled false | |
20 | + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | |
21 | + } | |
22 | + } | |
23 | +} | |
24 | + | |
25 | +dependencies { | |
26 | + compile fileTree(dir: 'libs', include: ['*.jar']) | |
27 | + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { | |
28 | + exclude group: 'com.android.support', module: 'support-annotations' | |
29 | + }) | |
30 | + compile 'com.android.support:appcompat-v7:25.1.1' | |
31 | + testCompile 'junit:junit:4.12' | |
32 | +} | ... | ... |
1 | +++ a/acrylic/proguard-rules.pro | |
... | ... | @@ -0,0 +1,17 @@ |
1 | +# Add project specific ProGuard rules here. | |
2 | +# By default, the flags in this file are appended to flags specified | |
3 | +# in C:\Users\Edmur\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt | |
4 | +# You can edit the include path and order by changing the proguardFiles | |
5 | +# directive in build.gradle. | |
6 | +# | |
7 | +# For more details, see | |
8 | +# http://developer.android.com/guide/developing/tools/proguard.html | |
9 | + | |
10 | +# Add any project specific keep options here: | |
11 | + | |
12 | +# If your project uses WebView with JS, uncomment the following | |
13 | +# and specify the fully qualified class name to the JavaScript interface | |
14 | +# class: | |
15 | +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | |
16 | +# public *; | |
17 | +#} | ... | ... |
acrylic/src/androidTest/java/anupam/acrylic/ExampleInstrumentedTest.java
0 → 100644
1 | +++ a/acrylic/src/androidTest/java/anupam/acrylic/ExampleInstrumentedTest.java | |
... | ... | @@ -0,0 +1,26 @@ |
1 | +package anupam.acrylic; | |
2 | + | |
3 | +import android.content.Context; | |
4 | +import android.support.test.InstrumentationRegistry; | |
5 | +import android.support.test.runner.AndroidJUnit4; | |
6 | + | |
7 | +import org.junit.Test; | |
8 | +import org.junit.runner.RunWith; | |
9 | + | |
10 | +import static org.junit.Assert.*; | |
11 | + | |
12 | +/** | |
13 | + * Instrumentation test, which will execute on an Android device. | |
14 | + * | |
15 | + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | |
16 | + */ | |
17 | +@RunWith(AndroidJUnit4.class) | |
18 | +public class ExampleInstrumentedTest { | |
19 | + @Test | |
20 | + public void useAppContext() throws Exception { | |
21 | + // Context of the app under test. | |
22 | + Context appContext = InstrumentationRegistry.getTargetContext(); | |
23 | + | |
24 | + assertEquals("anupam.acrylic", appContext.getPackageName()); | |
25 | + } | |
26 | +} | ... | ... |
1 | +++ a/acrylic/src/main/AndroidManifest.xml | |
... | ... | @@ -0,0 +1,49 @@ |
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + package="anupam.acrylic" | |
4 | + android:versionCode="15" | |
5 | + android:versionName="2.2.0" > | |
6 | + | |
7 | + <supports-screens | |
8 | + android:anyDensity="true" | |
9 | + android:largeScreens="true" | |
10 | + android:normalScreens="true" | |
11 | + android:resizeable="true" | |
12 | + android:smallScreens="true" /> | |
13 | + | |
14 | + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | |
15 | + | |
16 | + <application | |
17 | + android:allowBackup="true" | |
18 | + android:icon="@drawable/ic_launcher" | |
19 | + android:label="@string/app_name" | |
20 | + android:theme="@style/Theme" > | |
21 | + <activity | |
22 | + android:name=".Splash" | |
23 | + android:configChanges="keyboard|keyboardHidden|orientation" | |
24 | + android:theme="@android:style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen" > | |
25 | + <intent-filter> | |
26 | + <action android:name="android.intent.action.MAIN" /> | |
27 | + <category android:name="android.intent.category.LAUNCHER" /> | |
28 | + </intent-filter> | |
29 | + </activity> | |
30 | + <activity | |
31 | + android:name=".EasyPaint" | |
32 | + android:launchMode="singleTop" > | |
33 | + <intent-filter> | |
34 | + <action android:name="android.intent.action.SEND" /> | |
35 | + <category android:name="android.intent.category.DEFAULT" /> | |
36 | + <data android:mimeType="image/*" /> | |
37 | + </intent-filter> | |
38 | + </activity> | |
39 | + <activity | |
40 | + android:name=".AboutActivity" | |
41 | + android:label="@string/title_activity_about" | |
42 | + android:parentActivityName=".EasyPaint" > | |
43 | + <meta-data | |
44 | + android:name="android.support.PARENT_ACTIVITY" | |
45 | + android:value=".EasyPaint" /> | |
46 | + </activity> | |
47 | + </application> | |
48 | + | |
49 | +</manifest> | ... | ... |
acrylic/src/main/java/anupam/acrylic/AboutActivity.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/AboutActivity.java | |
... | ... | @@ -0,0 +1,21 @@ |
1 | +package anupam.acrylic; | |
2 | + | |
3 | +import android.app.Activity; | |
4 | +import android.os.Bundle; | |
5 | +import android.text.Html; | |
6 | +import android.text.Spanned; | |
7 | +import android.text.method.LinkMovementMethod; | |
8 | +import android.widget.TextView; | |
9 | + | |
10 | +public class AboutActivity extends Activity { | |
11 | + | |
12 | + @Override | |
13 | + protected void onCreate(Bundle savedInstanceState) { | |
14 | + super.onCreate(savedInstanceState); | |
15 | + setContentView(R.layout.activity_about); | |
16 | + Spanned htmlText = Html.fromHtml(getResources().getString(R.string.about_description)); | |
17 | + TextView aboutTextView = (TextView) findViewById(R.id.aboutTextView); | |
18 | + aboutTextView.setText(htmlText); | |
19 | + aboutTextView.setMovementMethod(LinkMovementMethod.getInstance()); | |
20 | + } | |
21 | +} | ... | ... |
acrylic/src/main/java/anupam/acrylic/ColorPickerDialog.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/ColorPickerDialog.java | |
... | ... | @@ -0,0 +1,242 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014 Valerio Bozzolan | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | +*/ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import android.annotation.SuppressLint; | |
21 | +import android.app.Dialog; | |
22 | +import android.content.Context; | |
23 | +import android.graphics.Canvas; | |
24 | +import android.graphics.Color; | |
25 | +import android.graphics.ColorMatrix; | |
26 | +import android.graphics.Paint; | |
27 | +import android.graphics.RectF; | |
28 | +import android.graphics.Shader; | |
29 | +import android.graphics.SweepGradient; | |
30 | +import android.os.Bundle; | |
31 | +import android.view.MotionEvent; | |
32 | +import android.view.View; | |
33 | + | |
34 | +@SuppressLint("ClickableViewAccessibility") | |
35 | +public class ColorPickerDialog extends Dialog { | |
36 | + | |
37 | + private OnColorChangedListener mListener; | |
38 | + private int mInitialColor; | |
39 | + | |
40 | + @Override | |
41 | + protected void onCreate(Bundle savedInstanceState) { | |
42 | + super.onCreate(savedInstanceState); | |
43 | + OnColorChangedListener l = new OnColorChangedListener() { | |
44 | + public void colorChanged(int color) { | |
45 | + mListener.colorChanged(color); | |
46 | + dismiss(); | |
47 | + } | |
48 | + }; | |
49 | + | |
50 | + setContentView(new ColorPickerView(getContext(), l, mInitialColor)); | |
51 | + setTitle(R.string.pick_color); | |
52 | + } | |
53 | + | |
54 | + public interface OnColorChangedListener { | |
55 | + void colorChanged(int color); | |
56 | + } | |
57 | + | |
58 | + private static class ColorPickerView extends View { | |
59 | + private static final int CENTER_X = 230; | |
60 | + private static final int CENTER_Y = 230; | |
61 | + private static final int CENTER_RADIUS = 100; | |
62 | + private Paint mPaint; | |
63 | + private Paint mCenterPaint; | |
64 | + private final int[] mColors; | |
65 | + private OnColorChangedListener mListener; | |
66 | + private boolean mTrackingCenter; | |
67 | + private boolean mHighlightCenter; | |
68 | + | |
69 | + ColorPickerView(Context c, OnColorChangedListener l, int color) { | |
70 | + super(c); | |
71 | + mListener = l; | |
72 | + mColors = new int[] { | |
73 | + 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, | |
74 | + 0xFFFFFF00, 0xFFFF0000 | |
75 | + }; | |
76 | + Shader s = new SweepGradient(0, 0, mColors, null); | |
77 | + | |
78 | + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); | |
79 | + mPaint.setShader(s); | |
80 | + mPaint.setStyle(Paint.Style.STROKE); | |
81 | + mPaint.setStrokeWidth(32); | |
82 | + | |
83 | + mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG); | |
84 | + mCenterPaint.setColor(color); | |
85 | + mCenterPaint.setStrokeWidth(EasyPaint.DEFAULT_BRUSH_SIZE); | |
86 | + } | |
87 | + | |
88 | + @Override | |
89 | + protected void onDraw(Canvas canvas) { | |
90 | + | |
91 | + float r = CENTER_X - mPaint.getStrokeWidth()*0.5f - 30; | |
92 | + | |
93 | + canvas.translate(CENTER_X, CENTER_X); | |
94 | + | |
95 | + canvas.drawOval(new RectF(-r, -r, r, r), mPaint); | |
96 | + canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint); | |
97 | + | |
98 | + if (mTrackingCenter) { | |
99 | + int c = mCenterPaint.getColor(); | |
100 | + mCenterPaint.setStyle(Paint.Style.STROKE); | |
101 | + | |
102 | + if (mHighlightCenter) { | |
103 | + mCenterPaint.setAlpha(0xFF); | |
104 | + } else { | |
105 | + mCenterPaint.setAlpha(0x80); | |
106 | + } | |
107 | + canvas.drawCircle(0, 0, | |
108 | + CENTER_RADIUS + mCenterPaint.getStrokeWidth(), | |
109 | + mCenterPaint); | |
110 | + | |
111 | + mCenterPaint.setStyle(Paint.Style.FILL); | |
112 | + mCenterPaint.setColor(c); | |
113 | + } | |
114 | + } | |
115 | + | |
116 | + @Override | |
117 | + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
118 | + setMeasuredDimension(CENTER_X*2, CENTER_Y*2); | |
119 | + } | |
120 | + | |
121 | + private int floatToByte(float x) { | |
122 | + int n = java.lang.Math.round(x); | |
123 | + return n; | |
124 | + } | |
125 | + private int pinToByte(int n) { | |
126 | + if (n < 0) { | |
127 | + n = 0; | |
128 | + } else if (n > 255) { | |
129 | + n = 255; | |
130 | + } | |
131 | + return n; | |
132 | + } | |
133 | + | |
134 | + private int ave(int s, int d, float p) { | |
135 | + return s + java.lang.Math.round(p * (d - s)); | |
136 | + } | |
137 | + | |
138 | + private int interpColor(int colors[], float unit) { | |
139 | + if (unit <= 0) { | |
140 | + return colors[0]; | |
141 | + } | |
142 | + if (unit >= 1) { | |
143 | + return colors[colors.length - 1]; | |
144 | + } | |
145 | + | |
146 | + float p = unit * (colors.length - 1); | |
147 | + int i = (int)p; | |
148 | + p -= i; | |
149 | + | |
150 | + // now p is just the fractional part [0...1) and i is the index | |
151 | + int c0 = colors[i]; | |
152 | + int c1 = colors[i+1]; | |
153 | + int a = ave(Color.alpha(c0), Color.alpha(c1), p); | |
154 | + int r = ave(Color.red(c0), Color.red(c1), p); | |
155 | + int g = ave(Color.green(c0), Color.green(c1), p); | |
156 | + int b = ave(Color.blue(c0), Color.blue(c1), p); | |
157 | + | |
158 | + return Color.argb(a, r, g, b); | |
159 | + } | |
160 | + | |
161 | + @SuppressWarnings("unused") | |
162 | + private int rotateColor(int color, float rad) { | |
163 | + float deg = rad * 180 / 3.1415927f; | |
164 | + int r = Color.red(color); | |
165 | + int g = Color.green(color); | |
166 | + int b = Color.blue(color); | |
167 | + | |
168 | + ColorMatrix cm = new ColorMatrix(); | |
169 | + ColorMatrix tmp = new ColorMatrix(); | |
170 | + | |
171 | + cm.setRGB2YUV(); | |
172 | + tmp.setRotate(0, deg); | |
173 | + cm.postConcat(tmp); | |
174 | + tmp.setYUV2RGB(); | |
175 | + cm.postConcat(tmp); | |
176 | + | |
177 | + final float[] a = cm.getArray(); | |
178 | + | |
179 | + int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b); | |
180 | + int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b); | |
181 | + int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b); | |
182 | + | |
183 | + return Color.argb(Color.alpha(color), pinToByte(ir), | |
184 | + pinToByte(ig), pinToByte(ib)); | |
185 | + } | |
186 | + | |
187 | + private static final float PI = 3.1415926f; | |
188 | + | |
189 | + @Override | |
190 | + public boolean onTouchEvent(MotionEvent event) { | |
191 | + float x = event.getX() - CENTER_X; | |
192 | + float y = event.getY() - CENTER_Y; | |
193 | + boolean inCenter = java.lang.Math.sqrt(x*x + y*y) <= CENTER_RADIUS; | |
194 | + | |
195 | + switch (event.getAction()) { | |
196 | + case MotionEvent.ACTION_DOWN: | |
197 | + mTrackingCenter = inCenter; | |
198 | + if (inCenter) { | |
199 | + mHighlightCenter = true; | |
200 | + invalidate(); | |
201 | + break; | |
202 | + } | |
203 | + case MotionEvent.ACTION_MOVE: | |
204 | + if (mTrackingCenter) { | |
205 | + if (mHighlightCenter != inCenter) { | |
206 | + mHighlightCenter = inCenter; | |
207 | + invalidate(); | |
208 | + } | |
209 | + } else { | |
210 | + float angle = (float)java.lang.Math.atan2(y, x); | |
211 | + // need to turn angle [-PI ... PI] into unit [0....1] | |
212 | + float unit = angle/(2*PI); | |
213 | + if (unit < 0) { | |
214 | + unit += 1; | |
215 | + } | |
216 | + mCenterPaint.setColor(interpColor(mColors, unit)); | |
217 | + invalidate(); | |
218 | + } | |
219 | + break; | |
220 | + case MotionEvent.ACTION_UP: | |
221 | + if (mTrackingCenter) { | |
222 | + if (inCenter) { | |
223 | + mListener.colorChanged(mCenterPaint.getColor()); | |
224 | + } | |
225 | + mTrackingCenter = false; // so we draw w/o halo | |
226 | + invalidate(); | |
227 | + } | |
228 | + break; | |
229 | + } | |
230 | + return true; | |
231 | + } | |
232 | + } | |
233 | + | |
234 | + public ColorPickerDialog(Context context, | |
235 | + OnColorChangedListener listener, | |
236 | + int initialColor) { | |
237 | + super(context); | |
238 | + | |
239 | + mListener = listener; | |
240 | + mInitialColor = initialColor; | |
241 | + } | |
242 | +} | ... | ... |
acrylic/src/main/java/anupam/acrylic/EasyPaint.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/EasyPaint.java | |
... | ... | @@ -0,0 +1,593 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014, 2016 Valerio Bozzolan & James Dearing (TheOpenSourceNinja) | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | + */ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import java.io.File; | |
21 | +import java.io.FileNotFoundException; | |
22 | +import java.io.FileOutputStream; | |
23 | +import java.io.IOException; | |
24 | +import java.util.Calendar; | |
25 | +import android.annotation.SuppressLint; | |
26 | +import android.app.AlertDialog; | |
27 | +import android.content.Context; | |
28 | +import android.content.DialogInterface; | |
29 | +import android.content.Intent; | |
30 | +import android.content.SharedPreferences; | |
31 | +import android.content.pm.ActivityInfo; | |
32 | +import android.graphics.Bitmap; | |
33 | +import android.graphics.Bitmap.CompressFormat; | |
34 | +import android.graphics.BitmapShader; | |
35 | +import android.graphics.BlurMaskFilter; | |
36 | +import android.graphics.Canvas; | |
37 | +import android.graphics.Color; | |
38 | +import android.graphics.EmbossMaskFilter; | |
39 | +import android.graphics.MaskFilter; | |
40 | +import android.graphics.Paint; | |
41 | +import android.graphics.Path; | |
42 | +import android.graphics.Point; | |
43 | +import android.graphics.PorterDuff.Mode; | |
44 | +import android.graphics.PorterDuffXfermode; | |
45 | +import android.graphics.PorterDuffColorFilter; | |
46 | +import android.graphics.Shader; | |
47 | +import android.net.Uri; | |
48 | +import android.os.Build; | |
49 | +import android.os.Bundle; | |
50 | +import android.os.Handler; | |
51 | +import android.provider.MediaStore; | |
52 | +import android.renderscript.Allocation; | |
53 | +import android.renderscript.Element; | |
54 | +import android.renderscript.RenderScript; | |
55 | +import android.renderscript.ScriptIntrinsicBlur; | |
56 | +import android.util.Log; | |
57 | +import android.view.Display; | |
58 | +import android.view.LayoutInflater; | |
59 | +import android.view.Menu; | |
60 | +import android.view.MenuItem; | |
61 | +import android.view.MotionEvent; | |
62 | +import android.view.View; | |
63 | +import android.view.ViewGroup; | |
64 | +import android.widget.SeekBar; | |
65 | +import android.widget.TextView; | |
66 | +import android.widget.Toast; | |
67 | + | |
68 | +@SuppressLint("ClickableViewAccessibility") | |
69 | +public class EasyPaint extends GraphicsActivity implements | |
70 | + ColorPickerDialog.OnColorChangedListener { | |
71 | + | |
72 | + public static int DEFAULT_BRUSH_SIZE = 10; | |
73 | + private static int MAX_POINTERS = 10; | |
74 | + private static final float TOUCH_TOLERANCE = 4; | |
75 | + | |
76 | + private Paint mPaint; | |
77 | + private MaskFilter mEmboss; | |
78 | + private MaskFilter mBlur; | |
79 | + private static final int CHOOSE_IMAGE = 0; | |
80 | + private MyView contentView; | |
81 | + | |
82 | + private boolean waitingForBackgroundColor = false; //If true and colorChanged() is called, fill the background, else mPaint.setColor() | |
83 | + private boolean extractingColor = false; //If this is true, the next touch event should extract a color rather than drawing a line. | |
84 | + | |
85 | + @Override | |
86 | + protected void onCreate(Bundle savedInstanceState) { | |
87 | + super.onCreate(savedInstanceState); | |
88 | + | |
89 | + // it removes the title from the actionbar(more space for icons?) | |
90 | + this.getActionBar().setDisplayShowTitleEnabled(false); | |
91 | + | |
92 | + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | |
93 | + contentView = new MyView( this ); | |
94 | + setContentView( contentView ); | |
95 | + | |
96 | + mPaint = new Paint(); | |
97 | + mPaint.setAntiAlias(true); | |
98 | + mPaint.setDither(true); | |
99 | + mPaint.setColor(Color.GREEN); | |
100 | + mPaint.setStyle(Paint.Style.STROKE); | |
101 | + mPaint.setStrokeJoin(Paint.Join.ROUND); | |
102 | + mPaint.setStrokeCap(Paint.Cap.ROUND); | |
103 | + mPaint.setStrokeWidth(DEFAULT_BRUSH_SIZE); | |
104 | + | |
105 | + // Where did these magic numbers come from? What do they mean? Can I change them? ~TheOpenSourceNinja | |
106 | + // Absolutely random numbers in order to see the emboss. asd! ~Valerio | |
107 | + mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f); | |
108 | + | |
109 | + mBlur = new BlurMaskFilter(5, BlurMaskFilter.Blur.NORMAL); | |
110 | + | |
111 | + if (isFirstTime()) { | |
112 | + AlertDialog.Builder alert = new AlertDialog.Builder(this); | |
113 | + | |
114 | + alert.setTitle(R.string.app_name); | |
115 | + alert.setMessage(R.string.app_description); | |
116 | + alert.setNegativeButton(R.string.continue_fuck, | |
117 | + new DialogInterface.OnClickListener() { | |
118 | + public void onClick(DialogInterface dialog, | |
119 | + int whichButton) { | |
120 | + Toast.makeText(getApplicationContext(), | |
121 | + R.string.here_is_your_canvas, | |
122 | + Toast.LENGTH_SHORT).show(); | |
123 | + } | |
124 | + }); | |
125 | + | |
126 | + alert.show(); | |
127 | + } else { | |
128 | + Toast.makeText(getApplicationContext(), | |
129 | + R.string.here_is_your_canvas, Toast.LENGTH_SHORT).show(); | |
130 | + } | |
131 | + | |
132 | + loadFromIntents(); | |
133 | + } | |
134 | + | |
135 | + @Override | |
136 | + public void onBackPressed() { | |
137 | + | |
138 | + super.onBackPressed(); | |
139 | + return; | |
140 | + } | |
141 | + public void colorChanged(int color) { | |
142 | + if( waitingForBackgroundColor ) { | |
143 | + waitingForBackgroundColor = false; | |
144 | + contentView.mBitmapBackground.eraseColor( color ); | |
145 | + //int[] colors = new int[ 1 ]; | |
146 | + //colors[ 0 ] = color; | |
147 | + //contentView.mBitmapBackground = Bitmap.createBitmap( colors, contentView.mBitmapBackground.getWidth(), contentView.mBitmapBackground.getHeight(), contentView.mBitmapBackground.getConfig() ); | |
148 | + } else { | |
149 | + mPaint.setColor( color ); | |
150 | + } | |
151 | + } | |
152 | + | |
153 | + public class MyView extends View { | |
154 | + | |
155 | + public Bitmap mBitmap; | |
156 | + private Bitmap mBitmapBackground; | |
157 | + private Canvas mCanvas; | |
158 | + private Paint mBitmapPaint; | |
159 | + private MultiLinePathManager multiLinePathManager; | |
160 | + | |
161 | + private class LinePath extends Path { | |
162 | + private Integer idPointer; | |
163 | + private float lastX; | |
164 | + private float lastY; | |
165 | + | |
166 | + LinePath() { | |
167 | + this.idPointer = null; | |
168 | + } | |
169 | + | |
170 | + public float getLastX() { | |
171 | + return lastX; | |
172 | + } | |
173 | + | |
174 | + public float getLastY() { | |
175 | + return lastY; | |
176 | + } | |
177 | + | |
178 | + public void touchStart(float x, float y) { | |
179 | + this.reset(); | |
180 | + this.moveTo(x, y); | |
181 | + this.lastX = x; | |
182 | + this.lastY = y; | |
183 | + } | |
184 | + | |
185 | + public void touchMove(float x, float y) { | |
186 | + float dx = Math.abs(x - lastX); | |
187 | + float dy = Math.abs(y - lastY); | |
188 | + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { | |
189 | + this.quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2); | |
190 | + lastX = x; | |
191 | + lastY = y; | |
192 | + } | |
193 | + } | |
194 | + | |
195 | + public boolean isDisassociatedFromPointer() { | |
196 | + return idPointer == null; | |
197 | + } | |
198 | + | |
199 | + public boolean isAssociatedToPointer(int idPointer) { | |
200 | + return this.idPointer != null | |
201 | + && (int) this.idPointer == idPointer; | |
202 | + } | |
203 | + | |
204 | + public void disassociateFromPointer() { | |
205 | + idPointer = null; | |
206 | + } | |
207 | + | |
208 | + public void associateToPointer(int idPointer) { | |
209 | + this.idPointer = idPointer; | |
210 | + } | |
211 | + } | |
212 | + | |
213 | + private class MultiLinePathManager { | |
214 | + public LinePath[] superMultiPaths; | |
215 | + | |
216 | + MultiLinePathManager(int maxPointers) { | |
217 | + superMultiPaths = new LinePath[maxPointers]; | |
218 | + for (int i = 0; i < maxPointers; i++) { | |
219 | + superMultiPaths[i] = new LinePath(); | |
220 | + } | |
221 | + } | |
222 | + | |
223 | + public LinePath findLinePathFromPointer(int idPointer) { | |
224 | + for (LinePath superMultiPath : superMultiPaths) { | |
225 | + if (superMultiPath.isAssociatedToPointer(idPointer)) { | |
226 | + return superMultiPath; | |
227 | + } | |
228 | + } | |
229 | + return null; | |
230 | + } | |
231 | + | |
232 | + public LinePath addLinePathWithPointer(int idPointer) { | |
233 | + for (LinePath superMultiPath : superMultiPaths) { | |
234 | + if (superMultiPath.isDisassociatedFromPointer()) { | |
235 | + superMultiPath.associateToPointer(idPointer); | |
236 | + return superMultiPath; | |
237 | + } | |
238 | + } | |
239 | + return null; | |
240 | + } | |
241 | + } | |
242 | + | |
243 | + public MyView(Context c) { | |
244 | + super(c); | |
245 | + | |
246 | + setId(R.id.CanvasId); | |
247 | + Display display = getWindowManager().getDefaultDisplay(); | |
248 | + Point size = new Point(); | |
249 | + display.getSize(size); | |
250 | + mBitmapBackground = Bitmap.createBitmap(size.x, size.y, Bitmap.Config.ARGB_8888); | |
251 | + mBitmap = Bitmap.createBitmap(size.x, size.y, | |
252 | + Bitmap.Config.ARGB_8888); | |
253 | + mCanvas = new Canvas(mBitmap); | |
254 | + mBitmapPaint = new Paint(Paint.DITHER_FLAG); | |
255 | + multiLinePathManager = new MultiLinePathManager(MAX_POINTERS); | |
256 | + } | |
257 | + | |
258 | + @Override | |
259 | + protected void onSizeChanged(int w, int h, int oldw, int oldh) { | |
260 | + super.onSizeChanged(w, h, oldw, oldh); | |
261 | + } | |
262 | + | |
263 | + @Override | |
264 | + protected void onDraw(Canvas canvas) { | |
265 | + canvas.drawColor(0xFFFFFFFF); | |
266 | + canvas.drawBitmap( mBitmapBackground, 0, 0, new Paint() ); | |
267 | + canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint ); | |
268 | + for (int i = 0; i < multiLinePathManager.superMultiPaths.length; i++) { | |
269 | + canvas.drawPath(multiLinePathManager.superMultiPaths[i], mPaint); | |
270 | + } | |
271 | + } | |
272 | + | |
273 | + @Override | |
274 | + public boolean onTouchEvent(MotionEvent event) { | |
275 | + LinePath linePath; | |
276 | + int index; | |
277 | + int id; | |
278 | + int eventMasked = event.getActionMasked(); | |
279 | + switch (eventMasked) { | |
280 | + case MotionEvent.ACTION_DOWN: | |
281 | + case MotionEvent.ACTION_POINTER_DOWN: { | |
282 | + index = event.getActionIndex( ); | |
283 | + id = event.getPointerId( index ); | |
284 | + | |
285 | + if( extractingColor ) { //If the user chose the 'extract color' menu option, the touch event indicates where they want to extract the color from. | |
286 | + extractingColor = false; | |
287 | + | |
288 | + View v = findViewById(R.id.CanvasId); | |
289 | + v.setDrawingCacheEnabled(true); | |
290 | + Bitmap cachedBitmap = v.getDrawingCache(); | |
291 | + | |
292 | + int newColor = cachedBitmap.getPixel( Math.round( event.getX( index ) ), Math.round( event.getY( index ) ) ); | |
293 | + | |
294 | + v.destroyDrawingCache(); | |
295 | + colorChanged( newColor ); | |
296 | + | |
297 | + Toast.makeText(getApplicationContext(), | |
298 | + R.string.color_extracted, | |
299 | + Toast.LENGTH_SHORT).show(); | |
300 | + } else { | |
301 | + | |
302 | + linePath = multiLinePathManager.addLinePathWithPointer( id ); | |
303 | + if( linePath != null ) { | |
304 | + linePath.touchStart( event.getX( index ), event.getY( index ) ); | |
305 | + } else { | |
306 | + Log.e( "anupam", "Too many fingers!" ); | |
307 | + } | |
308 | + } | |
309 | + | |
310 | + break; | |
311 | + } | |
312 | + case MotionEvent.ACTION_MOVE: | |
313 | + for (int i = 0; i < event.getPointerCount(); i++) { | |
314 | + id = event.getPointerId(i); | |
315 | + index = event.findPointerIndex(id); | |
316 | + linePath = multiLinePathManager.findLinePathFromPointer(id); | |
317 | + if (linePath != null) { | |
318 | + linePath.touchMove(event.getX(index), event.getY(index)); | |
319 | + } | |
320 | + } | |
321 | + break; | |
322 | + case MotionEvent.ACTION_UP: | |
323 | + case MotionEvent.ACTION_POINTER_UP: | |
324 | + case MotionEvent.ACTION_CANCEL: | |
325 | + index = event.getActionIndex(); | |
326 | + id = event.getPointerId(index); | |
327 | + linePath = multiLinePathManager.findLinePathFromPointer(id); | |
328 | + if (linePath != null) { | |
329 | + linePath.lineTo(linePath.getLastX(), linePath.getLastY()); | |
330 | + | |
331 | + // Commit the path to our offscreen | |
332 | + mCanvas.drawPath(linePath, mPaint); | |
333 | + | |
334 | + // Kill this so we don't double draw | |
335 | + linePath.reset(); | |
336 | + | |
337 | + // Allow this LinePath to be associated to another idPointer | |
338 | + linePath.disassociateFromPointer(); | |
339 | + } | |
340 | + break; | |
341 | + } | |
342 | + invalidate(); | |
343 | + return true; | |
344 | + } | |
345 | + } | |
346 | + | |
347 | + @Override | |
348 | + public boolean onCreateOptionsMenu(Menu menu) { | |
349 | + // Inflate the menu; this adds items to the action bar if it is present. | |
350 | + getMenuInflater().inflate(R.menu.main, menu); | |
351 | + return true; | |
352 | + } | |
353 | + | |
354 | + @Override | |
355 | + public boolean onPrepareOptionsMenu(Menu menu) { | |
356 | + super.onPrepareOptionsMenu(menu); | |
357 | + return true; | |
358 | + } | |
359 | + | |
360 | + @Override | |
361 | + public boolean onOptionsItemSelected(MenuItem item) { | |
362 | + mPaint.setXfermode(null); | |
363 | + mPaint.setAlpha(0xFF); | |
364 | + | |
365 | + switch (item.getItemId()) { | |
366 | + case R.id.normal_brush_menu: | |
367 | + mPaint.setShader( null ); | |
368 | + mPaint.setMaskFilter(null); | |
369 | + return true; | |
370 | + case R.id.color_menu: | |
371 | + new ColorPickerDialog(this, this, mPaint.getColor()).show(); | |
372 | + return true; | |
373 | + case R.id.emboss_menu: | |
374 | + mPaint.setShader( null ); | |
375 | + mPaint.setMaskFilter(mEmboss); | |
376 | + return true; | |
377 | + | |
378 | + case R.id.blur_menu: | |
379 | + mPaint.setShader( null ); | |
380 | + mPaint.setMaskFilter(mBlur); | |
381 | + return true; | |
382 | + case R.id.size_menu: { | |
383 | + LayoutInflater inflater = ( LayoutInflater ) getSystemService( Context.LAYOUT_INFLATER_SERVICE ); | |
384 | + View layout = inflater.inflate( R.layout.brush, | |
385 | + ( ViewGroup ) findViewById( R.id.root ) ); | |
386 | + AlertDialog.Builder builder = new AlertDialog.Builder( this ) | |
387 | + .setView( layout ); | |
388 | + builder.setTitle( R.string.choose_width ); | |
389 | + final AlertDialog alertDialog = builder.create( ); | |
390 | + alertDialog.show( ); | |
391 | + SeekBar sb = ( SeekBar ) layout.findViewById( R.id.brushSizeSeekBar ); | |
392 | + sb.setProgress( getStrokeSize( ) ); | |
393 | + final TextView txt = ( TextView ) layout | |
394 | + .findViewById( R.id.sizeValueTextView ); | |
395 | + txt.setText( String.format( | |
396 | + getResources( ).getString( R.string.your_selected_size_is ), | |
397 | + getStrokeSize( ) + 1 ) ); | |
398 | + sb.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener( ) { | |
399 | + public void onProgressChanged( SeekBar seekBar, | |
400 | + final int progress, boolean fromUser ) { | |
401 | + // Do something here with new value | |
402 | + mPaint.setStrokeWidth( progress ); | |
403 | + txt.setText( String.format( | |
404 | + getResources( ).getString( | |
405 | + R.string.your_selected_size_is ), progress + 1 ) ); | |
406 | + } | |
407 | + | |
408 | + @Override | |
409 | + public void onStartTrackingTouch( SeekBar seekBar ) { | |
410 | + // TODO Auto-generated method stub | |
411 | + } | |
412 | + | |
413 | + @Override | |
414 | + public void onStopTrackingTouch( SeekBar seekBar ) { | |
415 | + // TODO Auto-generated method stub | |
416 | + } | |
417 | + } ); | |
418 | + return true; | |
419 | + } | |
420 | + case R.id.erase_menu: { | |
421 | + LayoutInflater inflater_e = ( LayoutInflater ) getSystemService( Context.LAYOUT_INFLATER_SERVICE ); | |
422 | + View layout_e = inflater_e.inflate( R.layout.brush, | |
423 | + ( ViewGroup ) findViewById( R.id.root ) ); | |
424 | + AlertDialog.Builder builder_e = new AlertDialog.Builder( this ) | |
425 | + .setView( layout_e ); | |
426 | + builder_e.setTitle( R.string.choose_width ); | |
427 | + final AlertDialog alertDialog_e = builder_e.create( ); | |
428 | + alertDialog_e.show( ); | |
429 | + SeekBar sb_e = ( SeekBar ) layout_e.findViewById( R.id.brushSizeSeekBar ); | |
430 | + sb_e.setProgress( getStrokeSize( ) ); | |
431 | + final TextView txt_e = ( TextView ) layout_e | |
432 | + .findViewById( R.id.sizeValueTextView ); | |
433 | + txt_e.setText( String.format( | |
434 | + getResources( ).getString( R.string.your_selected_size_is ), | |
435 | + getStrokeSize( ) + 1 ) ); | |
436 | + sb_e.setOnSeekBarChangeListener( new SeekBar.OnSeekBarChangeListener( ) { | |
437 | + public void onProgressChanged( SeekBar seekBar, | |
438 | + final int progress, boolean fromUser ) { | |
439 | + // Do something here with new value | |
440 | + mPaint.setStrokeWidth( progress ); | |
441 | + txt_e.setText( String.format( | |
442 | + getResources( ).getString( | |
443 | + R.string.your_selected_size_is ), progress + 1 ) ); | |
444 | + } | |
445 | + | |
446 | + public void onStartTrackingTouch( SeekBar seekBar ) { | |
447 | + // TODO Auto-generated method stub | |
448 | + } | |
449 | + | |
450 | + public void onStopTrackingTouch( SeekBar seekBar ) { | |
451 | + // TODO Auto-generated method stub | |
452 | + } | |
453 | + } ); | |
454 | + mPaint.setShader( null ); | |
455 | + mPaint.setXfermode( new PorterDuffXfermode( Mode.CLEAR ) ); | |
456 | + return true; | |
457 | + } | |
458 | + case R.id.clear_all_menu: { | |
459 | + contentView.mBitmap.eraseColor( Color.TRANSPARENT ); | |
460 | + return true; | |
461 | + } | |
462 | + case R.id.save_menu: | |
463 | + takeScreenshot(true); | |
464 | + break; | |
465 | + case R.id.fill_background_with_color: { | |
466 | + waitingForBackgroundColor = true; | |
467 | + new ColorPickerDialog( this, this, contentView.mBitmapBackground.getPixel( 0, 0 ) ).show(); | |
468 | + return true; | |
469 | + } | |
470 | + case R.id.about_menu: | |
471 | + startActivity(new Intent(this, AboutActivity.class)); | |
472 | + break; | |
473 | + } | |
474 | + return super.onOptionsItemSelected(item); | |
475 | + } | |
476 | + | |
477 | + /** | |
478 | + * This takes the screenshot of the whole screen. Is this a good thing? | |
479 | + */ | |
480 | + private File takeScreenshot(boolean showToast) { | |
481 | + View v = findViewById(R.id.CanvasId); | |
482 | + v.setDrawingCacheEnabled(true); | |
483 | + Bitmap cachedBitmap = v.getDrawingCache(); | |
484 | + Bitmap copyBitmap = cachedBitmap.copy(Bitmap.Config.RGB_565, true); | |
485 | + v.destroyDrawingCache(); | |
486 | + FileOutputStream output = null; | |
487 | + File file = null; | |
488 | + try { | |
489 | + File path = Places.getScreenshotFolder(); | |
490 | + Calendar cal = Calendar.getInstance(); | |
491 | + | |
492 | + file = new File(path, | |
493 | + | |
494 | + cal.get(Calendar.YEAR) + "_" + (1 + cal.get(Calendar.MONTH)) + "_" | |
495 | + + cal.get(Calendar.DAY_OF_MONTH) + "_" | |
496 | + + cal.get(Calendar.HOUR_OF_DAY) + "_" | |
497 | + + cal.get(Calendar.MINUTE) + "_" + cal.get(Calendar.SECOND) | |
498 | + + ".png"); | |
499 | + output = new FileOutputStream(file); | |
500 | + copyBitmap.compress(CompressFormat.PNG, 100, output); | |
501 | + } catch (FileNotFoundException e) { | |
502 | + file = null; | |
503 | + e.printStackTrace(); | |
504 | + } finally { | |
505 | + if (output != null) { | |
506 | + try { | |
507 | + output.close(); | |
508 | + } catch (IOException e) { | |
509 | + e.printStackTrace(); | |
510 | + } | |
511 | + } | |
512 | + | |
513 | + } | |
514 | + | |
515 | + if (file != null) { | |
516 | + if (showToast) | |
517 | + Toast.makeText( | |
518 | + getApplicationContext(), | |
519 | + String.format( | |
520 | + getResources().getString( | |
521 | + R.string.saved_your_location_to), | |
522 | + file.getAbsolutePath()), Toast.LENGTH_LONG) | |
523 | + .show(); | |
524 | + // sending a broadcast to the media scanner so it will scan the new | |
525 | + // screenshot. | |
526 | + Intent requestScan = new Intent( | |
527 | + Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); | |
528 | + requestScan.setData(Uri.fromFile(file)); | |
529 | + sendBroadcast(requestScan); | |
530 | + | |
531 | + return file; | |
532 | + } else { | |
533 | + return null; | |
534 | + } | |
535 | + } | |
536 | + | |
537 | + private boolean isFirstTime() { | |
538 | + SharedPreferences preferences = getPreferences(MODE_PRIVATE); | |
539 | + boolean ranBefore = preferences.getBoolean("RanBefore", false); | |
540 | + if (!ranBefore) { | |
541 | + // first time | |
542 | + SharedPreferences.Editor editor = preferences.edit(); | |
543 | + editor.putBoolean("RanBefore", true); | |
544 | + editor.commit(); | |
545 | + } | |
546 | + return !ranBefore; | |
547 | + } | |
548 | + | |
549 | + private int getStrokeSize() { | |
550 | + return (int) mPaint.getStrokeWidth(); | |
551 | + } | |
552 | + | |
553 | + public void onActivityResult( int requestCode, int resultCode, Intent data ) { | |
554 | + super.onActivityResult( requestCode, resultCode, data ); | |
555 | + | |
556 | + if( resultCode != RESULT_CANCELED ) { //"The resultCode will be RESULT_CANCELED if the activity explicitly returned that, didn't return any result, or crashed during its operation." (quote from https://developer.android.com/reference/android/app/Activity.html#onActivityResult(int,%20int,%20android.content.Intent) ) | |
557 | + switch( requestCode ) { | |
558 | + case CHOOSE_IMAGE: { | |
559 | + setBackgroundUri( data.getData() ); | |
560 | + } | |
561 | + } | |
562 | + } | |
563 | + } | |
564 | + | |
565 | + public void setBackgroundUri(Uri uri) { | |
566 | + if (uri == null) { | |
567 | + return; | |
568 | + } | |
569 | + | |
570 | + try { | |
571 | + //I don't like loading both full-sized and reduced-size copies of the image (the larger copy can use a lot of memory), but I couldn't find any other way to do this. | |
572 | + Bitmap fullsize = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri); | |
573 | + Bitmap resized = Bitmap.createScaledBitmap(fullsize, contentView.mBitmap.getWidth(), contentView.mBitmap.getHeight(), true); | |
574 | + contentView.mBitmapBackground = resized; | |
575 | + //contentView.mCanvas = new Canvas( contentView.mBitmapBackground ); | |
576 | + } catch (IOException exception) { | |
577 | + //TODO: How should we handle this exception? | |
578 | + } | |
579 | + } | |
580 | + | |
581 | + public void loadFromIntents() { | |
582 | + Intent intent = getIntent(); | |
583 | + String action = intent.getAction(); | |
584 | + String type = intent.getType(); | |
585 | + System.out.println("Intentoso " + action + " type " + type); | |
586 | + if(Intent.ACTION_SEND.equals(action) && type != null) { | |
587 | + if( type.startsWith("image/") ) { | |
588 | + setBackgroundUri( (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM) ); | |
589 | + } | |
590 | + | |
591 | + } | |
592 | + } | |
593 | +} | ... | ... |
acrylic/src/main/java/anupam/acrylic/GraphicsActivity.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/GraphicsActivity.java | |
... | ... | @@ -0,0 +1,45 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014 Valerio Bozzolan | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | +*/ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import android.app.Activity; | |
21 | +import android.content.pm.ActivityInfo; | |
22 | +import android.os.Bundle; | |
23 | +import android.view.View; | |
24 | +import android.view.ViewGroup; | |
25 | + | |
26 | +class GraphicsActivity extends Activity { | |
27 | + @Override | |
28 | + protected void onCreate(Bundle savedInstanceState) { | |
29 | + super.onCreate(savedInstanceState); | |
30 | + this.setRequestedOrientation( | |
31 | + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | |
32 | + } | |
33 | + | |
34 | + @SuppressWarnings("unused") | |
35 | + @Override | |
36 | + public void setContentView(View view) { | |
37 | + if (false) { // set to true to test Picture | |
38 | + ViewGroup vg = new PictureLayout(this); | |
39 | + vg.addView(view); | |
40 | + view = vg; | |
41 | + } | |
42 | + super.setContentView(view); | |
43 | + } | |
44 | +} | |
45 | + | ... | ... |
acrylic/src/main/java/anupam/acrylic/MainActivity.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/MainActivity.java | |
... | ... | @@ -0,0 +1,13 @@ |
1 | +package anupam.acrylic; | |
2 | + | |
3 | +import android.support.v7.app.AppCompatActivity; | |
4 | +import android.os.Bundle; | |
5 | + | |
6 | +public class MainActivity extends AppCompatActivity { | |
7 | + | |
8 | + @Override | |
9 | + protected void onCreate(Bundle savedInstanceState) { | |
10 | + super.onCreate(savedInstanceState); | |
11 | + setContentView(R.layout.activity_main); | |
12 | + } | |
13 | +} | ... | ... |
acrylic/src/main/java/anupam/acrylic/PictureLayout.java
0 → 100644
1 | +++ a/acrylic/src/main/java/anupam/acrylic/PictureLayout.java | |
... | ... | @@ -0,0 +1,165 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014 Valerio Bozzolan | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | +*/ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import android.content.Context; | |
21 | +import android.graphics.Canvas; | |
22 | +import android.graphics.Picture; | |
23 | +import android.graphics.Rect; | |
24 | +import android.graphics.drawable.Drawable; | |
25 | +import android.util.AttributeSet; | |
26 | +import android.view.View; | |
27 | +import android.view.ViewGroup; | |
28 | +import android.view.ViewParent; | |
29 | + | |
30 | + | |
31 | +public class PictureLayout extends ViewGroup { | |
32 | + private static String error = "PictureLayout can host only one direct child"; | |
33 | + private final Picture mPicture = new Picture(); | |
34 | + | |
35 | + public PictureLayout(Context context) { | |
36 | + super(context); | |
37 | + } | |
38 | + | |
39 | + public PictureLayout(Context context, AttributeSet attrs) { | |
40 | + super(context, attrs); | |
41 | + } | |
42 | + | |
43 | + @Override | |
44 | + public void addView(View child) { | |
45 | + if (getChildCount() > 1) { | |
46 | + throw new IllegalStateException(error); | |
47 | + } | |
48 | + | |
49 | + super.addView(child); | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + public void addView(View child, int index) { | |
54 | + if (getChildCount() > 1) { | |
55 | + throw new IllegalStateException(error); | |
56 | + } | |
57 | + | |
58 | + super.addView(child, index); | |
59 | + } | |
60 | + | |
61 | + @Override | |
62 | + public void addView(View child, LayoutParams params) { | |
63 | + if (getChildCount() > 1) { | |
64 | + throw new IllegalStateException(error); | |
65 | + } | |
66 | + | |
67 | + super.addView(child, params); | |
68 | + } | |
69 | + | |
70 | + @Override | |
71 | + public void addView(View child, int index, LayoutParams params) { | |
72 | + if (getChildCount() > 1) { | |
73 | + throw new IllegalStateException(error); | |
74 | + } | |
75 | + | |
76 | + super.addView(child, index, params); | |
77 | + } | |
78 | + | |
79 | + @Override | |
80 | + protected LayoutParams generateDefaultLayoutParams() { | |
81 | + return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); | |
82 | + } | |
83 | + | |
84 | + @Override | |
85 | + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
86 | + final int count = getChildCount(); | |
87 | + | |
88 | + int maxHeight = 0; | |
89 | + int maxWidth = 0; | |
90 | + | |
91 | + for (int i = 0; i < count; i++) { | |
92 | + final View child = getChildAt(i); | |
93 | + if (child.getVisibility() != GONE) { | |
94 | + measureChild(child, widthMeasureSpec, heightMeasureSpec); | |
95 | + } | |
96 | + } | |
97 | + | |
98 | + maxWidth += getPaddingLeft() + getPaddingRight(); | |
99 | + maxHeight += getPaddingTop() + getPaddingBottom(); | |
100 | + | |
101 | + Drawable drawable = getBackground(); | |
102 | + if (drawable != null) { | |
103 | + maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); | |
104 | + maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); | |
105 | + } | |
106 | + | |
107 | + setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), | |
108 | + resolveSize(maxHeight, heightMeasureSpec)); | |
109 | + } | |
110 | + | |
111 | + private void drawPict(Canvas canvas, int x, int y, int w, int h, | |
112 | + float sx, float sy) { | |
113 | + canvas.save(); | |
114 | + canvas.translate(x, y); | |
115 | + canvas.clipRect(0, 0, w, h); | |
116 | + canvas.scale(0.5f, 0.5f); | |
117 | + canvas.scale(sx, sy, w, h); | |
118 | + canvas.drawPicture(mPicture); | |
119 | + canvas.restore(); | |
120 | + } | |
121 | + | |
122 | + @SuppressWarnings("unused") | |
123 | + @Override | |
124 | + protected void dispatchDraw(Canvas canvas) { | |
125 | + super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight())); | |
126 | + mPicture.endRecording(); | |
127 | + | |
128 | + int x = getWidth()/2; | |
129 | + int y = getHeight()/2; | |
130 | + | |
131 | + if (false) { | |
132 | + canvas.drawPicture(mPicture); | |
133 | + } else { | |
134 | + drawPict(canvas, 0, 0, x, y, 1, 1); | |
135 | + drawPict(canvas, x, 0, x, y, -1, 1); | |
136 | + drawPict(canvas, 0, y, x, y, 1, -1); | |
137 | + drawPict(canvas, x, y, x, y, -1, -1); | |
138 | + } | |
139 | + } | |
140 | + | |
141 | + @Override | |
142 | + public ViewParent invalidateChildInParent(int[] location, Rect dirty) { | |
143 | + location[0] = getLeft(); | |
144 | + location[1] = getTop(); | |
145 | + dirty.set(0, 0, getWidth(), getHeight()); | |
146 | + return getParent(); | |
147 | + } | |
148 | + | |
149 | + @Override | |
150 | + protected void onLayout(boolean changed, int l, int t, int r, int b) { | |
151 | + final int count = super.getChildCount(); | |
152 | + | |
153 | + for (int i = 0; i < count; i++) { | |
154 | + final View child = getChildAt(i); | |
155 | + if (child.getVisibility() != GONE) { | |
156 | + final int childLeft = getPaddingLeft(); | |
157 | + final int childTop = getPaddingTop(); | |
158 | + child.layout(childLeft, childTop, | |
159 | + childLeft + child.getMeasuredWidth(), | |
160 | + childTop + child.getMeasuredHeight()); | |
161 | + | |
162 | + } | |
163 | + } | |
164 | + } | |
165 | +} | ... | ... |
1 | +++ a/acrylic/src/main/java/anupam/acrylic/Places.java | |
... | ... | @@ -0,0 +1,53 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014 Valerio Bozzolan | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | +*/ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import java.io.File; | |
21 | +import java.io.IOException; | |
22 | + | |
23 | +import android.os.Environment; | |
24 | + | |
25 | +public class Places { | |
26 | + public static File getScreenshotFolder() { | |
27 | + File path = new File(Environment.getExternalStorageDirectory(), | |
28 | + "/Acrylic Paint/"); | |
29 | + path.mkdirs(); | |
30 | + | |
31 | + return path; | |
32 | + } | |
33 | + | |
34 | + public static File getCameraTempFolder() { | |
35 | + File path = new File(Environment.getExternalStorageDirectory(), | |
36 | + "/Acrylic Paint/Temp/"); | |
37 | + path.mkdirs(); | |
38 | + // this folder should not be scanned | |
39 | + File noScanning = new File(path, ".nomedia"); | |
40 | + if (!noScanning.exists()) | |
41 | + try { | |
42 | + noScanning.createNewFile(); | |
43 | + } catch (IOException e) { | |
44 | + // TODO Auto-generated catch block | |
45 | + e.printStackTrace(); | |
46 | + } | |
47 | + return path; | |
48 | + } | |
49 | + | |
50 | + public static File getCameraTempFile() { | |
51 | + return new File(getCameraTempFolder(), "temp.jpg"); | |
52 | + } | |
53 | +} | ... | ... |
1 | +++ a/acrylic/src/main/java/anupam/acrylic/Splash.java | |
... | ... | @@ -0,0 +1,51 @@ |
1 | +/* | |
2 | + * Copyright (C) 2014 Valerio Bozzolan | |
3 | + * | |
4 | + * This program is free software: you can redistribute it and/or modify | |
5 | + * it under the terms of the GNU General Public License as published by | |
6 | + * the Free Software Foundation, either version 3 of the License, or | |
7 | + * (at your option) any later version. | |
8 | + * | |
9 | + * This program is distributed in the hope that it will be useful, | |
10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | + * GNU General Public License for more details. | |
13 | + * | |
14 | + * You should have received a copy of the GNU General Public License | |
15 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | + */ | |
17 | + | |
18 | +package anupam.acrylic; | |
19 | + | |
20 | +import android.app.Activity; | |
21 | +import android.content.Intent; | |
22 | +import android.content.pm.ActivityInfo; | |
23 | +import android.os.Bundle; | |
24 | +import anupam.acrylic.R; | |
25 | + | |
26 | +public class Splash extends Activity { | |
27 | + | |
28 | + @Override | |
29 | + protected void onCreate(Bundle savedInstanceState) { | |
30 | + // TODO Auto-generated method stub | |
31 | + super.onCreate(savedInstanceState); | |
32 | + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); | |
33 | + setContentView(R.layout.main); | |
34 | + | |
35 | + Thread t = new Thread() { | |
36 | + | |
37 | + public void run() { | |
38 | + try { | |
39 | + Thread.sleep(1000); | |
40 | + | |
41 | + startActivity(new Intent().setClassName("anupam.acrylic", | |
42 | + "anupam.acrylic.EasyPaint")); | |
43 | + finish(); | |
44 | + } catch (Exception e) { | |
45 | + e.printStackTrace(); | |
46 | + } | |
47 | + } | |
48 | + }; | |
49 | + t.start(); | |
50 | + } | |
51 | +} | ... | ... |
1.71 KB
1022 Bytes
1.43 KB
1.26 KB
856 Bytes
406 Bytes
418 Bytes
689 Bytes
8.88 KB
448 Bytes
819 Bytes
1.21 KB
851 Bytes
874 Bytes
31.9 KB
1.11 KB
769 Bytes
937 Bytes
801 Bytes
619 Bytes
305 Bytes
276 Bytes
474 Bytes
6.09 KB
325 Bytes
563 Bytes
827 Bytes
596 Bytes
535 Bytes
19.4 KB
2.1 KB
1.11 KB
1.69 KB
1.53 KB
1.05 KB
554 Bytes
421 Bytes
921 Bytes
14.1 KB
540 Bytes
977 Bytes
1.63 KB
997 Bytes
1.11 KB
50.8 KB
3.77 KB
1.48 KB
2.71 KB
2.5 KB
1.58 KB
882 Bytes
766 Bytes
1.45 KB
22.9 KB
895 Bytes
1.32 KB
2.76 KB
1.49 KB
1.81 KB
79.1 KB
4.25 KB
5.05 KB
1.3 KB
2.94 KB
2.88 KB
1.71 KB
1006 Bytes
34.8 KB
1.24 KB
3.05 KB
1.38 KB
79.1 KB
1 | +++ a/acrylic/src/main/res/layout/activity_about.xml | |
... | ... | @@ -0,0 +1,20 @@ |
1 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
2 | + xmlns:tools="http://schemas.android.com/tools" | |
3 | + android:layout_width="match_parent" | |
4 | + android:layout_height="match_parent" | |
5 | + android:paddingBottom="@dimen/activity_vertical_margin" | |
6 | + android:paddingLeft="@dimen/activity_horizontal_margin" | |
7 | + android:paddingRight="@dimen/activity_horizontal_margin" | |
8 | + android:paddingTop="@dimen/activity_vertical_margin" | |
9 | + tools:context="anupam.acrylic.AboutActivity" > | |
10 | + | |
11 | + <TextView | |
12 | + android:id="@+id/aboutTextView" | |
13 | + android:layout_width="wrap_content" | |
14 | + android:layout_height="wrap_content" | |
15 | + android:layout_alignParentBottom="true" | |
16 | + android:layout_alignParentLeft="true" | |
17 | + android:layout_alignParentRight="true" | |
18 | + android:layout_alignParentTop="true" /> | |
19 | + | |
20 | +</RelativeLayout> | |
0 | 21 | \ No newline at end of file | ... | ... |
1 | +++ a/acrylic/src/main/res/layout/activity_main.xml | |
... | ... | @@ -0,0 +1,13 @@ |
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" | |
4 | + android:layout_width="match_parent" android:layout_height="match_parent" | |
5 | + android:paddingBottom="@dimen/activity_vertical_margin" | |
6 | + android:paddingLeft="@dimen/activity_horizontal_margin" | |
7 | + android:paddingRight="@dimen/activity_horizontal_margin" | |
8 | + android:paddingTop="@dimen/activity_vertical_margin" | |
9 | + tools:context="anupam.acrylic.MainActivity"> | |
10 | + | |
11 | + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" | |
12 | + android:text="Hello World!" /> | |
13 | +</RelativeLayout> | ... | ... |
1 | +++ a/acrylic/src/main/res/layout/brush.xml | |
... | ... | @@ -0,0 +1,31 @@ |
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + android:id="@+id/root" | |
4 | + android:layout_width="fill_parent" | |
5 | + android:layout_height="fill_parent" | |
6 | + android:orientation="vertical" > | |
7 | + | |
8 | + <SeekBar | |
9 | + android:id="@+id/brushSizeSeekBar" | |
10 | + android:layout_width="fill_parent" | |
11 | + android:layout_height="wrap_content" | |
12 | + android:layout_alignParentLeft="true" | |
13 | + android:layout_alignParentTop="true" | |
14 | + android:layout_marginTop="20dp" | |
15 | + android:max="99" | |
16 | + android:paddingLeft="30dp" | |
17 | + android:paddingRight="30dp" > | |
18 | + </SeekBar> | |
19 | + | |
20 | + <TextView | |
21 | + android:id="@+id/sizeValueTextView" | |
22 | + android:layout_width="wrap_content" | |
23 | + android:layout_height="wrap_content" | |
24 | + android:layout_alignParentLeft="true" | |
25 | + android:layout_alignParentRight="true" | |
26 | + android:layout_below="@+id/brushSizeSeekBar" | |
27 | + android:layout_marginTop="5dp" | |
28 | + android:layout_marginBottom="10dp" | |
29 | + android:gravity="center_horizontal" /> | |
30 | + | |
31 | +</RelativeLayout> | |
0 | 32 | \ No newline at end of file | ... | ... |
1 | +++ a/acrylic/src/main/res/layout/main.xml | |
... | ... | @@ -0,0 +1,8 @@ |
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
3 | + android:layout_width="fill_parent" | |
4 | + android:layout_height="fill_parent" | |
5 | + android:background="@drawable/splash" | |
6 | + android:orientation="vertical" > | |
7 | + | |
8 | +</LinearLayout> | |
0 | 9 | \ No newline at end of file | ... | ... |
1 | +++ a/acrylic/src/main/res/menu/main.xml | |
... | ... | @@ -0,0 +1,64 @@ |
1 | +<?xml version="1.0" encoding="utf-8"?> | |
2 | +<menu xmlns:android="http://schemas.android.com/apk/res/android" > | |
3 | + | |
4 | + <item | |
5 | + android:id="@+id/color_menu" | |
6 | + android:icon="@drawable/color" | |
7 | + android:showAsAction="ifRoom" | |
8 | + android:title="@string/color"/> | |
9 | + <item | |
10 | + android:id="@+id/size_menu" | |
11 | + android:icon="@drawable/size" | |
12 | + android:showAsAction="ifRoom" | |
13 | + android:title="@string/brush_size"> | |
14 | + </item> | |
15 | + <item | |
16 | + android:id="@+id/erase_menu" | |
17 | + android:icon="@drawable/erase" | |
18 | + android:showAsAction="ifRoom" | |
19 | + android:title="@string/erase"> | |
20 | + </item> | |
21 | + <item | |
22 | + android:id="@+id/clear_all_menu" | |
23 | + android:icon="@drawable/clear_all" | |
24 | + android:showAsAction="ifRoom" | |
25 | + android:title="@string/clear_all"> | |
26 | + </item> | |
27 | + <item | |
28 | + android:id="@+id/normal_brush_menu" | |
29 | + android:icon="@drawable/size" | |
30 | + android:showAsAction="ifRoom" | |
31 | + android:title="@string/normal"> | |
32 | + </item> | |
33 | + <item | |
34 | + android:id="@+id/emboss_menu" | |
35 | + android:icon="@drawable/emboss" | |
36 | + android:showAsAction="ifRoom" | |
37 | + android:title="@string/emboss"> | |
38 | + </item> | |
39 | + <item | |
40 | + android:id="@+id/blur_menu" | |
41 | + android:icon="@drawable/blur" | |
42 | + android:showAsAction="ifRoom" | |
43 | + android:title="@string/blur"> | |
44 | + </item> | |
45 | + <item | |
46 | + android:id="@+id/save_menu" | |
47 | + android:icon="@drawable/save" | |
48 | + android:showAsAction="ifRoom" | |
49 | + android:title="@string/save"> | |
50 | + </item> | |
51 | + <item | |
52 | + android:id="@+id/fill_background_with_color" | |
53 | + android:icon="@drawable/fill_background_with_color" | |
54 | + android:showAsAction="ifRoom" | |
55 | + android:title="@string/fill_background_with_color"> | |
56 | + </item> | |
57 | + <item | |
58 | + android:id="@+id/about_menu" | |
59 | + android:icon="@drawable/about" | |
60 | + android:showAsAction="ifRoom" | |
61 | + android:title="@string/about"> | |
62 | + </item> | |
63 | + | |
64 | +</menu> | ... | ... |
3.34 KB
2.15 KB