• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.egg.paint;
18 
19 import static android.view.MotionEvent.ACTION_CANCEL;
20 import static android.view.MotionEvent.ACTION_DOWN;
21 import static android.view.MotionEvent.ACTION_MOVE;
22 import static android.view.MotionEvent.ACTION_UP;
23 
24 import android.app.Activity;
25 import android.content.res.Configuration;
26 import android.graphics.Color;
27 import android.os.Bundle;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.WindowManager;
32 import android.view.animation.OvershootInterpolator;
33 import android.widget.FrameLayout;
34 import android.widget.ImageButton;
35 import android.widget.LinearLayout;
36 import android.widget.Magnifier;
37 
38 import com.android.egg.R;
39 
40 import java.util.Arrays;
41 import java.util.stream.IntStream;
42 
43 public class PaintActivity extends Activity {
44     private static final float MAX_BRUSH_WIDTH_DP = 100f;
45     private static final float MIN_BRUSH_WIDTH_DP = 1f;
46 
47     private static final int NUM_BRUSHES = 6;
48     private static final int NUM_COLORS = 6;
49 
50     private Painting painting = null;
51     private CutoutAvoidingToolbar toolbar = null;
52     private LinearLayout brushes = null;
53     private LinearLayout colors = null;
54     private Magnifier magnifier = null;
55     private boolean sampling = false;
56 
57     private View.OnClickListener buttonHandler = new View.OnClickListener() {
58         @Override
59         public void onClick(View view) {
60             // With non final fields in the R class we can't switch on the
61             // id since the case values are no longer constants.
62             int viewId = view.getId();
63             if (viewId == R.id.btnBrush) {
64                 view.setSelected(true);
65                 hideToolbar(colors);
66                 toggleToolbar(brushes);
67             } else if (viewId == R.id.btnColor) {
68                 view.setSelected(true);
69                 hideToolbar(brushes);
70                 toggleToolbar(colors);
71             } else if (viewId == R.id.btnClear) {
72                 painting.clear();
73             } else if (viewId == R.id.btnSample) {
74                 sampling = true;
75                 view.setSelected(true);
76             } else if (viewId == R.id.btnZen) {
77                 painting.setZenMode(!painting.getZenMode());
78                 view.animate()
79                         .setStartDelay(200)
80                         .setInterpolator(new OvershootInterpolator())
81                         .rotation(painting.getZenMode() ? 0f : 90f);
82             }
83         }
84     };
85 
showToolbar(View bar)86     private void showToolbar(View bar) {
87         if (bar.getVisibility() != View.GONE) return;
88         bar.setVisibility(View.VISIBLE);
89         bar.setTranslationY(toolbar.getHeight()/2);
90         bar.animate()
91                 .translationY(toolbar.getHeight())
92                 .alpha(1f)
93                 .setDuration(220)
94                 .start();
95     }
96 
hideToolbar(View bar)97     private void hideToolbar(View bar) {
98         if (bar.getVisibility() != View.VISIBLE) return;
99         bar.animate()
100                 .translationY(toolbar.getHeight()/2)
101                 .alpha(0f)
102                 .setDuration(150)
103                 .withEndAction(new Runnable() {
104                     @Override
105                     public void run() {
106                         bar.setVisibility(View.GONE);
107                     }
108                 })
109                 .start();
110     }
111 
toggleToolbar(View bar)112     private void toggleToolbar(View bar) {
113         if (bar.getVisibility() == View.VISIBLE) {
114             hideToolbar(bar);
115         } else {
116             showToolbar(bar);
117         }
118     }
119 
120     private BrushPropertyDrawable widthButtonDrawable;
121     private BrushPropertyDrawable colorButtonDrawable;
122     private float maxBrushWidth, minBrushWidth;
123     private int nightMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
124 
lerp(float f, float a, float b)125     static final float lerp(float f, float a, float b) {
126         return a + (b-a) * f;
127     }
128 
setupViews(Painting oldPainting)129     void setupViews(Painting oldPainting) {
130         setContentView(R.layout.activity_paint);
131 
132         painting = oldPainting != null ? oldPainting : new Painting(this);
133         ((FrameLayout) findViewById(R.id.contentView)).addView(painting,
134                 new FrameLayout.LayoutParams(
135                         ViewGroup.LayoutParams.MATCH_PARENT,
136                         ViewGroup.LayoutParams.MATCH_PARENT));
137 
138         painting.setPaperColor(getColor(R.color.paper_color));
139         painting.setPaintColor(getColor(R.color.paint_color));
140 
141         toolbar = findViewById(R.id.toolbar);
142         brushes = findViewById(R.id.brushes);
143         colors = findViewById(R.id.colors);
144 
145         magnifier = new Magnifier(painting);
146 
147         painting.setOnTouchListener(
148                 new View.OnTouchListener() {
149                     @Override
150                     public boolean onTouch(View view, MotionEvent event) {
151                         switch (event.getActionMasked()) {
152                             case ACTION_DOWN:
153                             case ACTION_MOVE:
154                                 if (sampling) {
155                                     magnifier.show(event.getX(), event.getY());
156                                     colorButtonDrawable.setWellColor(
157                                             painting.sampleAt(event.getX(), event.getY()));
158                                     return true;
159                                 }
160                                 break;
161                             case ACTION_CANCEL:
162                                 if (sampling) {
163                                     findViewById(R.id.btnSample).setSelected(false);
164                                     sampling = false;
165                                     magnifier.dismiss();
166                                 }
167                                 break;
168                             case ACTION_UP:
169                                 if (sampling) {
170                                     findViewById(R.id.btnSample).setSelected(false);
171                                     sampling = false;
172                                     magnifier.dismiss();
173                                     painting.setPaintColor(
174                                             painting.sampleAt(event.getX(), event.getY()));
175                                     refreshBrushAndColor();
176                                 }
177                                 break;
178                         }
179                         return false; // allow view to continue handling
180                     }
181                 });
182 
183         findViewById(R.id.btnBrush).setOnClickListener(buttonHandler);
184         findViewById(R.id.btnColor).setOnClickListener(buttonHandler);
185         findViewById(R.id.btnClear).setOnClickListener(buttonHandler);
186         findViewById(R.id.btnSample).setOnClickListener(buttonHandler);
187         findViewById(R.id.btnZen).setOnClickListener(buttonHandler);
188 
189         findViewById(R.id.btnColor).setOnLongClickListener(new View.OnLongClickListener() {
190             @Override
191             public boolean onLongClick(View view) {
192                 colors.removeAllViews();
193                 showToolbar(colors);
194                 refreshBrushAndColor();
195                 return true;
196             }
197         });
198 
199         findViewById(R.id.btnClear).setOnLongClickListener(new View.OnLongClickListener() {
200             @Override
201             public boolean onLongClick(View view) {
202                 painting.invertContents();
203                 return true;
204             }
205         });
206 
207         widthButtonDrawable = new BrushPropertyDrawable(this);
208         widthButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
209         colorButtonDrawable = new BrushPropertyDrawable(this);
210         colorButtonDrawable.setFrameColor(getColor(R.color.toolbar_icon_color));
211 
212         ((ImageButton) findViewById(R.id.btnBrush)).setImageDrawable(widthButtonDrawable);
213         ((ImageButton) findViewById(R.id.btnColor)).setImageDrawable(colorButtonDrawable);
214 
215         refreshBrushAndColor();
216     }
217 
refreshBrushAndColor()218     private void refreshBrushAndColor() {
219         final LinearLayout.LayoutParams button_lp = new LinearLayout.LayoutParams(
220                 0, ViewGroup.LayoutParams.WRAP_CONTENT);
221         button_lp.weight = 1f;
222         if (brushes.getChildCount() == 0) {
223             for (int i = 0; i < NUM_BRUSHES; i++) {
224                 final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
225                 icon.setFrameColor(getColor(R.color.toolbar_icon_color));
226                 // exponentially increasing brush size
227                 final float width = lerp(
228                         (float) Math.pow((float) i / NUM_BRUSHES, 2f), minBrushWidth,
229                         maxBrushWidth);
230                 icon.setWellScale(width / maxBrushWidth);
231                 icon.setWellColor(getColor(R.color.toolbar_icon_color));
232                 final ImageButton button = new ImageButton(this);
233                 button.setImageDrawable(icon);
234                 button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
235                 button.setOnClickListener(
236                         new View.OnClickListener() {
237                             @Override
238                             public void onClick(View view) {
239                                 brushes.setSelected(false);
240                                 hideToolbar(brushes);
241                                 painting.setBrushWidth(width);
242                                 refreshBrushAndColor();
243                             }
244                         });
245                 brushes.addView(button, button_lp);
246             }
247         }
248 
249         if (colors.getChildCount() == 0) {
250             final Palette pal = new Palette(NUM_COLORS);
251             for (final int c : IntStream.concat(
252                     IntStream.of(Color.BLACK, Color.WHITE),
253                     Arrays.stream(pal.getColors())
254             ).toArray()) {
255                 final BrushPropertyDrawable icon = new BrushPropertyDrawable(this);
256                 icon.setFrameColor(getColor(R.color.toolbar_icon_color));
257                 icon.setWellColor(c);
258                 final ImageButton button = new ImageButton(this);
259                 button.setImageDrawable(icon);
260                 button.setBackground(getDrawable(R.drawable.toolbar_button_bg));
261                 button.setOnClickListener(
262                     new View.OnClickListener() {
263                         @Override
264                         public void onClick(View view) {
265                             colors.setSelected(false);
266                             hideToolbar(colors);
267                             painting.setPaintColor(c);
268                             refreshBrushAndColor();
269                         }
270                     });
271                 colors.addView(button, button_lp);
272             }
273         }
274 
275         widthButtonDrawable.setWellScale(painting.getBrushWidth() / maxBrushWidth);
276         widthButtonDrawable.setWellColor(painting.getPaintColor());
277         colorButtonDrawable.setWellColor(painting.getPaintColor());
278     }
279 
refreshNightMode(Configuration config)280     private void refreshNightMode(Configuration config) {
281         int newNightMode =
282                 (config.uiMode & Configuration.UI_MODE_NIGHT_MASK);
283         if (nightMode != newNightMode) {
284             if (nightMode != Configuration.UI_MODE_NIGHT_UNDEFINED) {
285                 painting.invertContents();
286 
287                 ((ViewGroup) painting.getParent()).removeView(painting);
288                 setupViews(painting);
289 
290                 final View decorView = getWindow().getDecorView();
291                 int decorSUIV = decorView.getSystemUiVisibility();
292 
293                 if (newNightMode == Configuration.UI_MODE_NIGHT_YES) {
294                     decorView.setSystemUiVisibility(
295                             decorSUIV & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
296                 } else {
297                     decorView.setSystemUiVisibility(
298                             decorSUIV | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
299                 }
300             }
301             nightMode = newNightMode;
302         }
303     }
304 
PaintActivity()305     public PaintActivity() {
306 
307     }
308 
309     @Override
onTrimMemory(int level)310     public void onTrimMemory(int level) {
311         super.onTrimMemory(level);
312 
313         painting.onTrimMemory();
314     }
315 
316     @Override
onConfigurationChanged(Configuration newConfig)317     public void onConfigurationChanged(Configuration newConfig) {
318         super.onConfigurationChanged(newConfig);
319 
320         refreshNightMode(newConfig);
321     }
322 
323     @Override
onCreate(Bundle savedInstanceState)324     public void onCreate(Bundle savedInstanceState) {
325         super.onCreate(savedInstanceState);
326 
327         WindowManager.LayoutParams lp = getWindow().getAttributes();
328         lp.flags = lp.flags
329                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
330                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
331         getWindow().setAttributes(lp);
332 
333         maxBrushWidth = MAX_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
334         minBrushWidth = MIN_BRUSH_WIDTH_DP * getResources().getDisplayMetrics().density;
335 
336         setupViews(null);
337         refreshNightMode(getResources().getConfiguration());
338     }
339 
340     @Override
onPostResume()341     public void onPostResume() {
342         super.onPostResume();
343     }
344 
345 }
346