1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package unlock; 33 34 import java.net.URL; 35 import java.util.ResourceBundle; 36 37 import javafx.animation.FadeTransition; 38 import javafx.animation.ParallelTransition; 39 import javafx.animation.SequentialTransition; 40 import javafx.animation.Transition; 41 import javafx.event.ActionEvent; 42 import javafx.fxml.FXML; 43 import javafx.scene.Node; 44 import javafx.scene.control.Button; 45 import javafx.scene.input.KeyEvent; 46 import javafx.scene.shape.Rectangle; 47 import javafx.util.Callback; 48 import javafx.util.Duration; 49 50 /** 51 * The controller for our 'Unlock' application, see 'Unlock.fxml'. 52 * This class has all the logic to open the theater's doors using JavaFX 53 * transitions. 54 */ 55 public final class UnlockController { 56 57 @FXML // ResourceBundle that was given to the FXMLLoader 58 private ResourceBundle resources; 59 @FXML // URL location of the FXML file that was given to the FXMLLoader 60 private URL location; 61 @FXML 62 private Node root; 63 @FXML // fx:id="pad" 64 private Keypad pad; // Value injected by FXMLLoader 65 @FXML // fx:id="error" 66 private Rectangle error; // Value injected by FXMLLoader 67 @FXML // fx:id="lock" 68 private Button lock; // Value injected by FXMLLoader 69 @FXML // fx:id="okleft" 70 private Rectangle okleft; // Value injected by FXMLLoader 71 @FXML // fx:id="okright" 72 private Rectangle okright; // Value injected by FXMLLoader 73 @FXML // fx:id="unlockbottom" 74 private Rectangle unlockbottom; // Value injected by FXMLLoader 75 @FXML // fx:id="unlocktop" 76 private Rectangle unlocktop; // Value injected by FXMLLoader 77 private boolean open = false; 78 79 private final static class HeightTransition extends Transition { 80 81 final Rectangle node; 82 final double height; 83 84 public HeightTransition(Duration duration, Rectangle node) { 85 this(duration, node, node.getHeight()); 86 } 87 88 public HeightTransition(Duration duration, Rectangle node, double height) { 89 this.node = node; 90 this.height = height; 91 this.setCycleDuration(duration); 92 } 93 94 public Duration getDuration() { 95 return getCycleDuration(); 96 } 97 98 @Override 99 protected void interpolate(double frac) { 100 this.node.setHeight((1.0 - frac) * height); 101 } 102 } 103 104 private final static class HeightAndLayoutYTransition extends Transition { 105 106 final Rectangle node; 107 final double height; 108 final double layoutY; 109 110 public HeightAndLayoutYTransition(Duration duration, Rectangle node) { 111 this(duration, node, node.getHeight(), node.getLayoutY()); 112 } 113 114 public HeightAndLayoutYTransition(Duration duration, Rectangle node, double height, double layoutY) { 115 this.node = node; 116 this.height = height; 117 this.layoutY = layoutY; 118 this.setCycleDuration(duration); 119 } 120 121 public Duration getDuration() { 122 return getCycleDuration(); 123 } 124 125 @Override 126 protected void interpolate(double frac) { 127 this.node.setHeight((1.0 - frac) * height); 128 this.node.setLayoutY((1.0 + frac) * layoutY); 129 } 130 } 131 132 private final static class WidthTransition extends Transition { 133 134 final Rectangle node; 135 final double width; 136 137 public WidthTransition(Duration duration, Rectangle node) { 138 this(duration, node, node.getWidth()); 139 } 140 141 public WidthTransition(Duration duration, Rectangle node, double width) { 142 this.node = node; 143 this.width = width; 144 this.setCycleDuration(duration); 145 } 146 147 public Duration getDuration() { 148 return getCycleDuration(); 149 } 150 151 @Override 152 protected void interpolate(double frac) { 153 this.node.setWidth((1.0 - frac) * width); 154 } 155 } 156 157 private final static class WidthAndLayoutXTransition extends Transition { 158 159 final Rectangle node; 160 final double width; 161 final double layoutX; 162 163 public WidthAndLayoutXTransition(Duration duration, Rectangle node) { 164 this(duration, node, node.getWidth(), node.getLayoutX()); 165 } 166 167 public WidthAndLayoutXTransition(Duration duration, Rectangle node, double width, double layoutX) { 168 this.node = node; 169 this.width = width; 170 this.layoutX = layoutX; 171 this.setCycleDuration(duration); 172 } 173 174 public Duration getDuration() { 175 return getCycleDuration(); 176 } 177 178 @Override 179 protected void interpolate(double frac) { 180 this.node.setWidth((1.0 - frac) * width); 181 this.node.setLayoutX((1.0 + frac) * layoutX); 182 } 183 } 184 185 private FadeTransition fadeOut(final Duration duration, final Node node) { 186 final FadeTransition fadeOut = new FadeTransition(duration, node); 187 fadeOut.setFromValue(1); 188 fadeOut.setToValue(0); 189 fadeOut.setOnFinished(arg0 -> node.setVisible(false)); 190 return fadeOut; 191 } 192 193 // Handler for Button[fx:id="lock"] onAction 194 @FXML 195 void unlockPressed(ActionEvent event) { 196 // handle the event here 197 lock.setDisable(true); 198 root.requestFocus(); 199 200 final FadeTransition fadeLockButton = fadeOut(Duration.valueOf("1s"), lock); 201 final HeightTransition openLockTop = new HeightTransition(Duration.valueOf("2s"), unlocktop); 202 openLockTop.setOnFinished(arg0 -> { 203 unlocktop.setVisible(false); 204 unlocktop.setHeight(openLockTop.height); 205 }); 206 207 final HeightAndLayoutYTransition openLockBottom = new HeightAndLayoutYTransition(Duration.valueOf("2s"), unlockbottom); 208 openLockBottom.setOnFinished(arg0 -> { 209 unlockbottom.setVisible(false); 210 unlockbottom.setHeight(openLockBottom.height); 211 unlockbottom.setLayoutY(openLockBottom.layoutY); 212 }); 213 final ParallelTransition openLock = new ParallelTransition(openLockTop, openLockBottom); 214 final SequentialTransition unlock = new SequentialTransition(fadeLockButton, openLock); 215 unlock.play(); 216 } 217 218 private void resetVisibility() { 219 pad.setOpacity(1.0); 220 lock.setOpacity(1.0); 221 pad.setVisible(true); 222 lock.setVisible(true); 223 lock.setDisable(false); 224 okright.setVisible(true); 225 okleft.setVisible(true); 226 unlocktop.setVisible(true); 227 unlockbottom.setVisible(true); 228 } 229 230 // Handler for AnchorPane[id="AnchorPane"] onKeyPressed 231 private void keyboardKeyPressed(KeyEvent event) { 232 if (" ".equals(event.getCharacter())) { 233 // When "Hello World" is displayed (the theater is open) - pressing 234 // the space bar will reinitialize the application. 235 if (open) { 236 // Reinitializing the application... 237 open = false; 238 resetVisibility(); 239 lock.requestFocus(); 240 } 241 } 242 } 243 244 private final class ValidateCallback implements Callback<String, Boolean> { 245 private ValidateCallback() { 246 } 247 @Override 248 public Boolean call(String param) { 249 final boolean accessGranted = "1234".equals(param); 250 if (accessGranted) { 251 grantAccess(); 252 } else { 253 rejectAccess(); 254 } 255 return accessGranted; 256 } 257 258 } 259 260 @FXML // This method is called by the FXMLLoader when initialization is complete 261 void initialize() { 262 assert error != null : "fx:id=\"error\" was not injected: check your FXML file 'Unlock.fxml'."; 263 assert lock != null : "fx:id=\"lock\" was not injected: check your FXML file 'Unlock.fxml'."; 264 assert okleft != null : "fx:id=\"okleft\" was not injected: check your FXML file 'Unlock.fxml'."; 265 assert okright != null : "fx:id=\"okright\" was not injected: check your FXML file 'Unlock.fxml'."; 266 assert pad != null : "fx:id=\"pad\" was not injected: check your FXML file 'Unlock.fxml'."; 267 assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'Unlock.fxml'."; 268 assert unlockbottom != null : "fx:id=\"unlockbottom\" was not injected: check your FXML file 'Unlock.fxml'."; 269 assert unlocktop != null : "fx:id=\"unlocktop\" was not injected: check your FXML file 'Unlock.fxml'."; 270 271 // Set pin validation for the keypad 272 pad.setValidateCallback(new ValidateCallback()); 273 274 // Reset visibility and opacity of nodes - useful if you left your 275 // FXML in a 'bad' state 276 resetVisibility(); 277 278 // Add event handler to the root - used to handle the space bar key at the 279 // end of the application 280 root.addEventHandler(KeyEvent.KEY_TYPED, event -> { 281 if (event instanceof KeyEvent) { 282 keyboardKeyPressed((KeyEvent) event); 283 } 284 }); 285 } 286 287 private void grantAccess() { 288 root.requestFocus(); 289 FadeTransition fadeOutPad = fadeOut(Duration.valueOf("1s"), pad); 290 291 final WidthTransition openOkLeft = 292 new WidthTransition(Duration.valueOf("2s"), okleft); 293 openOkLeft.setOnFinished(arg0 -> { 294 okleft.setVisible(false); 295 okleft.setWidth(openOkLeft.width); 296 }); 297 298 final WidthAndLayoutXTransition openOkRight = 299 new WidthAndLayoutXTransition(openOkLeft.getDuration(), okright); 300 openOkRight.setOnFinished(arg0 -> { 301 okright.setVisible(false); 302 okright.setWidth(openOkRight.width); 303 okright.setLayoutX(openOkRight.layoutX); 304 }); 305 306 final ParallelTransition openOk = 307 new ParallelTransition(openOkLeft, openOkRight); 308 309 final SequentialTransition okTrans = 310 new SequentialTransition(fadeOutPad, openOk); 311 okTrans.setOnFinished(arg0 -> { 312 open = true; 313 root.requestFocus(); 314 }); 315 okTrans.play(); 316 } 317 318 private void rejectAccess() { 319 FadeTransition errorTrans = new FadeTransition(Duration.valueOf("500ms"), error); 320 errorTrans.setFromValue(0.0); 321 errorTrans.setToValue(1.0); 322 errorTrans.setCycleCount(2); 323 errorTrans.setAutoReverse(true); 324 errorTrans.play(); 325 } 326 }