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