Dan Sandler | 45f17c5 | 2018-05-02 20:01:38 -0400 | [diff] [blame] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 131 | static final float lerp(float f, float a, float b) { |
| 132 | return a + (b-a) * f; |
| 133 | } |
| 134 | |
| 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 | |
| 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 | |
| 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 | |
| 311 | public PaintActivity() { |
| 312 | |
| 313 | } |
| 314 | |
| 315 | @Override |
| 316 | public void onTrimMemory(int level) { |
| 317 | super.onTrimMemory(level); |
| 318 | |
| 319 | painting.onTrimMemory(); |
| 320 | } |
| 321 | |
| 322 | @Override |
| 323 | public void onConfigurationChanged(Configuration newConfig) { |
| 324 | super.onConfigurationChanged(newConfig); |
| 325 | |
| 326 | refreshNightMode(newConfig); |
| 327 | } |
| 328 | |
| 329 | @Override |
| 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 |
| 347 | public void onPostResume() { |
| 348 | super.onPostResume(); |
| 349 | } |
| 350 | |
| 351 | } |