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