1 /* 2 * Copyright (C) 2010 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.camera.ui; 18 19 import android.graphics.Rect; 20 import android.view.View.MeasureSpec; 21 import android.view.animation.AlphaAnimation; 22 import android.view.animation.Animation; 23 import android.view.animation.AnimationSet; 24 import android.view.animation.OvershootInterpolator; 25 import android.view.animation.ScaleAnimation; 26 27 import javax.microedition.khronos.opengles.GL11; 28 29 class PopupWindow extends GLView { 30 31 protected BitmapTexture mAnchor; 32 protected int mAnchorOffset; 33 34 protected int mAnchorPosition; 35 private final RotatePane mRotatePane = new RotatePane(); 36 private RawTexture mBackupTexture; 37 38 protected Texture mBackground; 39 private boolean mUsingStencil; 40 41 public PopupWindow() { 42 super.addComponent(mRotatePane); 43 } 44 45 @Override 46 protected void onAttachToRoot(GLRootView root) { 47 super.onAttachToRoot(root); 48 mUsingStencil = root.getEGLConfigChooser().getStencilBits() > 0; 49 } 50 51 public void setBackground(Texture background) { 52 if (background == mBackground) return; 53 mBackground = background; 54 if (background != null && background instanceof NinePatchTexture) { 55 setPaddings(((NinePatchTexture) mBackground).getPaddings()); 56 } else { 57 setPaddings(0, 0, 0, 0); 58 } 59 invalidate(); 60 } 61 62 public void setAnchor(BitmapTexture anchor, int offset) { 63 mAnchor = anchor; 64 mAnchorOffset = offset; 65 } 66 67 @Override 68 public void addComponent(GLView component) { 69 throw new UnsupportedOperationException("use setContent(GLView)"); 70 } 71 72 @Override 73 protected void onMeasure(int widthSpec, int heightSpec) { 74 int widthMode = MeasureSpec.getMode(widthSpec); 75 if (widthMode != MeasureSpec.UNSPECIFIED) { 76 Rect p = mPaddings; 77 int width = MeasureSpec.getSize(widthSpec); 78 widthSpec = MeasureSpec.makeMeasureSpec( 79 Math.max(0, width - p.left - p.right 80 - mAnchor.getWidth() + mAnchorOffset), widthMode); 81 } 82 83 int heightMode = MeasureSpec.getMode(heightSpec); 84 if (heightMode != MeasureSpec.UNSPECIFIED) { 85 int height = MeasureSpec.getSize(widthSpec); 86 widthSpec = MeasureSpec.makeMeasureSpec(Math.max( 87 0, height - mPaddings.top - mPaddings.bottom), heightMode); 88 } 89 90 Rect p = mPaddings; 91 GLView child = mRotatePane; 92 child.measure(widthSpec, heightSpec); 93 setMeasuredSize(child.getMeasuredWidth() 94 + p.left + p.right + mAnchor.getWidth() - mAnchorOffset, 95 child.getMeasuredHeight() + p.top + p.bottom); 96 } 97 98 @Override 99 protected void onLayout( 100 boolean change, int left, int top, int right, int bottom) { 101 Rect p = getPaddings(); 102 GLView view = mRotatePane; 103 view.layout(p.left, p.top, 104 getWidth() - p.right - mAnchor.getWidth() + mAnchorOffset, 105 getHeight() - p.bottom); 106 } 107 108 public void setAnchorPosition(int yoffset) { 109 mAnchorPosition = yoffset; 110 } 111 112 private void renderBackgroundWithStencil(GLRootView root, GL11 gl) { 113 int width = getWidth(); 114 int height = getHeight(); 115 int aWidth = mAnchor.getWidth(); 116 int aHeight = mAnchor.getHeight(); 117 118 Rect p = mPaddings; 119 int aXoffset = width - aWidth; 120 int aYoffset = Math.max(p.top, mAnchorPosition - aHeight / 2); 121 aYoffset = Math.min(aYoffset, height - p.bottom - aHeight); 122 123 if (mAnchor != null) { 124 gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); 125 gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1); 126 mAnchor.draw(root, aXoffset, aYoffset); 127 gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1); 128 gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); 129 } 130 131 if (mBackground != null) { 132 mBackground.draw(root, 0, 0, 133 width - aWidth + mAnchorOffset, height); 134 } 135 } 136 137 private void renderBackgroundWithoutStencil(GLRootView root, GL11 gl) { 138 int width = getWidth(); 139 int height = getHeight(); 140 int aWidth = mAnchor.getWidth(); 141 int aHeight = mAnchor.getHeight(); 142 143 Rect p = mPaddings; 144 int aXoffset = width - aWidth; 145 int aYoffset = Math.max(p.top, mAnchorPosition - aHeight / 2); 146 aYoffset = Math.min(aYoffset, height - p.bottom - aHeight); 147 148 if (mAnchor != null) { 149 mAnchor.draw(root, aXoffset, aYoffset); 150 } 151 152 if (mBackupTexture == null || mBackupTexture.getBoundGL() != gl) { 153 mBackupTexture = RawTexture.newInstance(gl); 154 } 155 156 RawTexture backup = mBackupTexture; 157 try { 158 // Copy the current drawing results of the triangle area into 159 // "backup", so that we can restore the content after it is 160 // overlaid by the background. 161 root.copyTexture2D(backup, aXoffset, aYoffset, aWidth, aHeight); 162 } catch (GLOutOfMemoryException e) { 163 e.printStackTrace(); 164 } 165 166 if (mBackground != null) { 167 mBackground.draw(root, 0, 0, 168 width - aWidth + mAnchorOffset, height); 169 } 170 171 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO); 172 backup.drawBack(root, aXoffset, aYoffset, aWidth, aHeight); 173 gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); 174 } 175 176 @Override 177 protected void renderBackground(GLRootView root, GL11 gl) { 178 if (mUsingStencil) { 179 renderBackgroundWithStencil(root, gl); 180 } else { 181 renderBackgroundWithoutStencil(root, gl); 182 } 183 } 184 185 public void setContent(GLView content) { 186 mRotatePane.setContent(content); 187 } 188 189 @Override 190 public void clearComponents() { 191 throw new UnsupportedOperationException(); 192 } 193 194 public void popup() { 195 setVisibility(GLView.VISIBLE); 196 197 AnimationSet set = new AnimationSet(false); 198 Animation scale = new ScaleAnimation( 199 0.7f, 1f, 0.7f, 1f, getWidth(), mAnchorPosition); 200 Animation alpha = new AlphaAnimation(0.5f, 1.0f); 201 202 set.addAnimation(scale); 203 set.addAnimation(alpha); 204 scale.setDuration(150); 205 alpha.setDuration(100); 206 scale.setInterpolator(new OvershootInterpolator()); 207 startAnimation(set); 208 } 209 210 public void popoff() { 211 setVisibility(GLView.INVISIBLE); 212 Animation alpha = new AlphaAnimation(0.7f, 0.0f); 213 alpha.setDuration(100); 214 startAnimation(alpha); 215 } 216 217 public void setOrientation(int orientation) { 218 switch (orientation) { 219 case 90: 220 mRotatePane.setOrientation(RotatePane.LEFT); 221 break; 222 case 180: 223 mRotatePane.setOrientation(RotatePane.DOWN); 224 break; 225 case 270: 226 mRotatePane.setOrientation(RotatePane.RIGHT); 227 break; 228 default: 229 mRotatePane.setOrientation(RotatePane.UP); 230 break; 231 } 232 } 233 } 234