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