1 /* 2 * Copyright (C) 2019 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.example.android.multidisplay.wallpaper; 18 19 20 import android.app.WallpaperColors; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapFactory; 23 import android.graphics.Canvas; 24 import android.graphics.Color; 25 import android.graphics.Paint; 26 import android.graphics.Point; 27 import android.graphics.Rect; 28 import android.graphics.drawable.ColorDrawable; 29 import android.hardware.display.DisplayManager; 30 import android.hardware.display.DisplayManager.DisplayListener; 31 import android.os.Handler; 32 import android.service.wallpaper.WallpaperService; 33 import android.util.DisplayMetrics; 34 import android.view.Display; 35 import android.view.MotionEvent; 36 import android.view.SurfaceHolder; 37 import android.view.WindowManager; 38 import java.util.Random; 39 import com.example.android.multidisplay.R; 40 41 public class SampleWallpaper extends WallpaperService { 42 43 @Override onCreateEngine()44 public Engine onCreateEngine() { 45 return new MySampleEngine(); 46 } 47 48 private class MySampleEngine extends Engine { 49 private boolean mVisible = false; 50 private DisplayMetrics mDisplayMetrics; 51 private Display mDisplay; 52 private Paint mPaint = new Paint(); 53 54 private final Handler mHandler = new Handler(); 55 private final Runnable mDrawRunner = this::draw; 56 57 private String mShowingText; 58 private final Rect mTextBounds = new Rect(); 59 60 private Bitmap mTipImage; 61 62 private final Point mCircleShift = new Point(); 63 private final Point mCirclePosition = new Point(); 64 private float mCircleRadioShift; 65 private final float MaxCircleRadioShift = 6f; 66 private boolean mRadioRevert = false; 67 68 private int mBackgroundColor = Color.BLACK; 69 private int mPaintColor = Color.WHITE; 70 71 private boolean mCircleDirectionX = false; 72 private boolean mCircleDirectionY = false; 73 74 @Override onCreate(SurfaceHolder surfaceHolder)75 public void onCreate(SurfaceHolder surfaceHolder) { 76 initDisplay(); 77 updateDisplay(); 78 initPaint(); 79 genNewShift(); 80 genNewColor(); 81 mHandler.post(mDrawRunner); 82 } 83 84 @Override onDestroy()85 public void onDestroy() { 86 final DisplayManager dm = getSystemService(DisplayManager.class); 87 if (dm != null) { 88 dm.unregisterDisplayListener(mDisplayListener); 89 } 90 } 91 92 @Override onComputeColors()93 public WallpaperColors onComputeColors() { 94 super.onComputeColors(); 95 ColorDrawable drawable = new ColorDrawable(mBackgroundColor); 96 drawable.setBounds(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.widthPixels); 97 return WallpaperColors.fromDrawable(drawable); 98 } 99 100 @Override onVisibilityChanged(boolean visible)101 public void onVisibilityChanged(boolean visible) { 102 mVisible = visible; 103 if (visible) { 104 mHandler.post(mDrawRunner); 105 } else { 106 mHandler.removeCallbacks(mDrawRunner); 107 } 108 } 109 110 @Override onTouchEvent(MotionEvent event)111 public void onTouchEvent(MotionEvent event) { 112 if (event.getAction() == MotionEvent.ACTION_DOWN) { 113 mCirclePosition.x = (int) event.getX(); 114 mCirclePosition.y = (int) event.getY(); 115 invertCircleDirectionIfNeeded(); 116 } 117 super.onTouchEvent(event); 118 } 119 120 @Override onSurfaceDestroyed(SurfaceHolder holder)121 public void onSurfaceDestroyed(SurfaceHolder holder) { 122 super.onSurfaceDestroyed(holder); 123 mVisible = false; 124 mHandler.removeCallbacks(mDrawRunner); 125 } 126 draw()127 private void draw() { 128 SurfaceHolder holder = getSurfaceHolder(); 129 Canvas canvas = null; 130 float centerX = (float) mDisplayMetrics.widthPixels/2; 131 float centerY = (float) mDisplayMetrics.heightPixels/2; 132 133 updateShift(); 134 invertCircleDirectionIfNeeded(); 135 136 try { 137 canvas = holder.lockCanvas(); 138 if (canvas != null) { 139 canvas.drawColor(mBackgroundColor); 140 if (mTipImage != null) { 141 canvas.drawBitmap(mTipImage, 0, 0, mPaint); 142 } 143 canvas.drawText(mShowingText, centerX - mTextBounds.exactCenterX(), 144 centerY - mTextBounds.exactCenterY(), mPaint); 145 146 canvas.drawCircle(mCirclePosition.x, mCirclePosition.y, 147 20.0f + mCircleRadioShift, mPaint); 148 } 149 } finally { 150 if (canvas != null) 151 holder.unlockCanvasAndPost(canvas); 152 } 153 mHandler.removeCallbacks(mDrawRunner); 154 if (mVisible) { 155 mHandler.postDelayed(mDrawRunner, 40); 156 } 157 } 158 invertCircleDirectionIfNeeded()159 private void invertCircleDirectionIfNeeded() { 160 boolean invertX = mCirclePosition.x < 0 161 || mCirclePosition.x > mDisplayMetrics.widthPixels; 162 boolean invertY = mCirclePosition.y < 0 163 || mCirclePosition.y > mDisplayMetrics.heightPixels; 164 165 if (!invertX && !invertY) return; 166 167 if (invertX) { 168 mCircleDirectionX = mCirclePosition.x < 0; 169 } 170 if (invertY) { 171 mCircleDirectionY = mCirclePosition.y < 0; 172 } 173 174 genNewShift(); 175 genNewColor(); 176 } 177 178 private void updateShift() { 179 mCirclePosition.x = mCircleDirectionX 180 ? mCirclePosition.x + mCircleShift.x 181 : mCirclePosition.x - mCircleShift.x; 182 mCirclePosition.y = mCircleDirectionY 183 ? mCirclePosition.y + mCircleShift.y 184 : mCirclePosition.y - mCircleShift.y; 185 186 mCircleRadioShift = mRadioRevert ? mCircleRadioShift + 1f : mCircleRadioShift - 1f; 187 if (Math.abs(mCircleRadioShift) > MaxCircleRadioShift) { 188 mRadioRevert = !mRadioRevert; 189 } 190 } 191 192 private void genNewShift() { 193 Random random = new Random(); 194 mCircleShift.x = Math.abs(random.nextInt(5)); 195 mCircleShift.y = Math.abs(5 - mCircleShift.x); 196 } 197 198 private void genNewColor() { 199 final Random random = new Random(); 200 int br = random.nextInt(256); 201 int bg = random.nextInt(256); 202 int bb = random.nextInt(256); 203 204 // Keep some contrast... 205 int pg = Math.abs(bg - 128); 206 int pr = Math.abs(br - 128); 207 int pb = Math.abs(bb - 128); 208 mBackgroundColor = Color.argb(255, br, bg, bb); 209 mPaintColor = Color.argb(255, pr, pg, pb); 210 mPaint.setColor(mPaintColor); 211 } 212 213 private void initDisplay() { 214 // If we want to get display, use getDisplayContext().getSystemService so the 215 // WindowManager is created for this context. 216 final WindowManager wm = getDisplayContext().getSystemService(WindowManager.class); 217 if (wm != null) { 218 mDisplay = wm.getDefaultDisplay(); 219 } 220 final DisplayManager dm = getSystemService(DisplayManager.class); 221 if (dm != null) { 222 dm.registerDisplayListener(mDisplayListener, null); 223 } 224 } 225 226 private void updateDisplay() { 227 // Use getDisplayContext() to get the context for current display. 228 mDisplayMetrics = getDisplayContext().getResources().getDisplayMetrics(); 229 mCirclePosition.x = mDisplayMetrics.widthPixels/2; 230 mCirclePosition.y = mDisplayMetrics.heightPixels/2 + 60; 231 232 mShowingText = "densityDpi= " + mDisplayMetrics.densityDpi; 233 if (mTipImage != null) { 234 mTipImage.recycle(); 235 mTipImage = null; 236 } 237 mTipImage = BitmapFactory 238 .decodeResource(getDisplayContext().getResources(), R.drawable.res_image); 239 mPaint.getTextBounds(mShowingText, 0, mShowingText.length(), mTextBounds); 240 } 241 242 public MySampleEngine() { 243 244 } 245 246 private void initPaint() { 247 mPaint.setAntiAlias(true); 248 mPaint.setStrokeWidth(1f); 249 mPaint.setTextSize(50f); 250 } 251 252 // Use DisplayListener to know display changed. 253 private final DisplayListener mDisplayListener = new DisplayListener() { 254 @Override 255 public void onDisplayChanged(int displayId) { 256 if (mDisplay.getDisplayId() == displayId) { 257 updateDisplay(); 258 } 259 } 260 261 @Override 262 public void onDisplayRemoved(int displayId) { 263 // handle here or wait onDestroy 264 } 265 266 @Override 267 public void onDisplayAdded(int displayId) { 268 } 269 }; 270 } 271 } 272