From 357d63f39856d85f072ae08f232291c97a2db4e3 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 14 May 2020 09:03:13 +0200 Subject: [PATCH] Added animation to unlock dialog --- .../ui/mainwindow/WelcomeController.java | 1 + .../ui/unlock/UnlockController.java | 72 ++++++++++++++++++ .../main/resources/fxml/addvault_welcome.fxml | 2 +- .../resources/fxml/preferences_about.fxml | 2 +- main/ui/src/main/resources/fxml/unlock.fxml | 37 +++++++-- .../resources/fxml/vault_detail_welcome.fxml | 2 +- main/ui/src/main/resources/img/bot/arm-l.png | Bin 0 -> 568 bytes .../src/main/resources/img/bot/arm-l@2x.png | Bin 0 -> 1122 bytes main/ui/src/main/resources/img/bot/arm-r.png | Bin 0 -> 572 bytes .../src/main/resources/img/bot/arm-r@2x.png | Bin 0 -> 1122 bytes main/ui/src/main/resources/img/bot/body.png | Bin 0 -> 1418 bytes .../ui/src/main/resources/img/bot/body@2x.png | Bin 0 -> 2769 bytes .../src/main/resources/img/{ => bot}/bot.png | Bin .../main/resources/img/{ => bot}/bot@2x.png | Bin main/ui/src/main/resources/img/bot/face.png | Bin 0 -> 508 bytes .../ui/src/main/resources/img/bot/face@2x.png | Bin 0 -> 937 bytes main/ui/src/main/resources/img/bot/legs.png | Bin 0 -> 648 bytes .../ui/src/main/resources/img/bot/legs@2x.png | Bin 0 -> 1164 bytes 18 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 main/ui/src/main/resources/img/bot/arm-l.png create mode 100644 main/ui/src/main/resources/img/bot/arm-l@2x.png create mode 100644 main/ui/src/main/resources/img/bot/arm-r.png create mode 100644 main/ui/src/main/resources/img/bot/arm-r@2x.png create mode 100644 main/ui/src/main/resources/img/bot/body.png create mode 100644 main/ui/src/main/resources/img/bot/body@2x.png rename main/ui/src/main/resources/img/{ => bot}/bot.png (100%) rename main/ui/src/main/resources/img/{ => bot}/bot@2x.png (100%) create mode 100644 main/ui/src/main/resources/img/bot/face.png create mode 100644 main/ui/src/main/resources/img/bot/face@2x.png create mode 100644 main/ui/src/main/resources/img/bot/legs.png create mode 100644 main/ui/src/main/resources/img/bot/legs@2x.png diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/WelcomeController.java index 2646c1a36..95a65f6ac 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/WelcomeController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/WelcomeController.java @@ -42,4 +42,5 @@ public class WelcomeController implements FxController { public boolean isNoVaultPresent() { return noVaultPresent.get(); } + } diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index e08164167..7e887ced4 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -1,5 +1,10 @@ package org.cryptomator.ui.unlock; +import javafx.animation.Animation; +import javafx.animation.Interpolator; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.ObjectBinding; @@ -9,7 +14,11 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.scene.control.CheckBox; import javafx.scene.control.ContentDisplay; +import javafx.scene.image.ImageView; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Translate; import javafx.stage.Stage; +import javafx.util.Duration; import org.cryptomator.common.vaults.Vault; import org.cryptomator.keychain.KeychainManager; import org.cryptomator.ui.common.FxController; @@ -42,8 +51,15 @@ public class UnlockController implements FxController { private final ObjectBinding unlockButtonContentDisplay; private final BooleanBinding userInteractionDisabled; private final BooleanProperty unlockButtonDisabled; + public NiceSecurePasswordField passwordField; public CheckBox savePasswordCheckbox; + public ImageView face; + public ImageView leftArm; + public ImageView rightArm; + public ImageView legs; + public ImageView body; + public Animation unlockAnimation; @Inject public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, AtomicReference password, @Named("savePassword") AtomicBoolean savePassword, @Named("savedPassword") Optional savedPassword, UserInteractionLock passwordEntryLock, ForgetPasswordComponent.Builder forgetPassword, Optional keychain) { @@ -60,12 +76,51 @@ public class UnlockController implements FxController { this.unlockButtonDisabled = new SimpleBooleanProperty(); } + @FXML public void initialize() { savePasswordCheckbox.setSelected(savedPassword.isPresent()); if (password.get() != null) { passwordField.setPassword(password.get()); } unlockButtonDisabled.bind(userInteractionDisabled.or(passwordField.textProperty().isEmpty())); + + var leftArmTranslation = new Translate(24, 0); + var leftArmRotation = new Rotate(60, 16, 30, 0); + var leftArmRetracted = new KeyValue(leftArmTranslation.xProperty(), 24); + var leftArmExtended = new KeyValue(leftArmTranslation.xProperty(), 0.0); + var leftArmHorizontal = new KeyValue(leftArmRotation.angleProperty(), 60, Interpolator.EASE_OUT); + var leftArmHanging = new KeyValue(leftArmRotation.angleProperty(), 0); + leftArm.getTransforms().setAll(leftArmTranslation, leftArmRotation); + + var rightArmTranslation = new Translate(-24, 0); + var rightArmRotation = new Rotate(60, 48, 30, 0); + var rightArmRetracted = new KeyValue(rightArmTranslation.xProperty(), -24); + var rightArmExtended = new KeyValue(rightArmTranslation.xProperty(), 0.0); + var rightArmHorizontal = new KeyValue(rightArmRotation.angleProperty(), -60); + var rightArmHanging = new KeyValue(rightArmRotation.angleProperty(), 0, Interpolator.EASE_OUT); + rightArm.getTransforms().setAll(rightArmTranslation, rightArmRotation); + + var legsRetractedY = new KeyValue(legs.scaleYProperty(), 0); + var legsExtendedY = new KeyValue(legs.scaleYProperty(), 1, Interpolator.EASE_OUT); + var legsRetractedX = new KeyValue(legs.scaleXProperty(), 0); + var legsExtendedX = new KeyValue(legs.scaleXProperty(), 1, Interpolator.EASE_OUT); + legs.setScaleY(0); + legs.setScaleX(0); + + var faceHidden = new KeyValue(face.opacityProperty(), 0.0); + var faceVisible = new KeyValue(face.opacityProperty(), 1.0, Interpolator.LINEAR); + face.setOpacity(0); + + unlockAnimation = new Timeline( + new KeyFrame(Duration.ZERO, leftArmRetracted, leftArmHorizontal, rightArmRetracted, rightArmHorizontal, legsRetractedY, legsRetractedX, faceHidden), + new KeyFrame(Duration.millis(200), leftArmExtended, leftArmHorizontal, rightArmRetracted, rightArmHorizontal), + new KeyFrame(Duration.millis(400), leftArmExtended, leftArmHanging, rightArmExtended, rightArmHorizontal), + new KeyFrame(Duration.millis(600), leftArmExtended, leftArmHanging, rightArmExtended, rightArmHanging), + new KeyFrame(Duration.millis(800), legsExtendedY, legsExtendedX, faceHidden), + new KeyFrame(Duration.millis(1000), faceVisible) + ); + + passwordEntryLock.awaitingInteraction().addListener(observable -> stopUnlockAnimation()); } @FXML @@ -88,6 +143,23 @@ public class UnlockController implements FxController { Arrays.fill(oldPw, ' '); } passwordEntryLock.interacted(UnlockModule.PasswordEntry.PASSWORD_ENTERED); + startUnlockAnimation(); + } + + private void startUnlockAnimation() { + leftArm.setVisible(true); + rightArm.setVisible(true); + legs.setVisible(true); + face.setVisible(true); + unlockAnimation.playFromStart(); + } + + private void stopUnlockAnimation() { + unlockAnimation.stop(); + leftArm.setVisible(false); + rightArm.setVisible(false); + legs.setVisible(false); + face.setVisible(false); } /* Save Password */ diff --git a/main/ui/src/main/resources/fxml/addvault_welcome.fxml b/main/ui/src/main/resources/fxml/addvault_welcome.fxml index 78fef4647..1639beac4 100644 --- a/main/ui/src/main/resources/fxml/addvault_welcome.fxml +++ b/main/ui/src/main/resources/fxml/addvault_welcome.fxml @@ -21,7 +21,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/preferences_about.fxml b/main/ui/src/main/resources/fxml/preferences_about.fxml index d388663e5..ba738f25e 100644 --- a/main/ui/src/main/resources/fxml/preferences_about.fxml +++ b/main/ui/src/main/resources/fxml/preferences_about.fxml @@ -18,7 +18,7 @@ - + diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml index 099a04148..331eeade8 100644 --- a/main/ui/src/main/resources/fxml/unlock.fxml +++ b/main/ui/src/main/resources/fxml/unlock.fxml @@ -5,6 +5,10 @@ + + + + @@ -19,11 +23,34 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main/ui/src/main/resources/fxml/vault_detail_welcome.fxml b/main/ui/src/main/resources/fxml/vault_detail_welcome.fxml index 50ba0c3c6..d9b6ca9fc 100644 --- a/main/ui/src/main/resources/fxml/vault_detail_welcome.fxml +++ b/main/ui/src/main/resources/fxml/vault_detail_welcome.fxml @@ -11,8 +11,8 @@ spacing="24"> + - diff --git a/main/ui/src/main/resources/img/bot/arm-l.png b/main/ui/src/main/resources/img/bot/arm-l.png new file mode 100644 index 0000000000000000000000000000000000000000..ecaab44f12ca5dad7bfa5c1833aae855d46b931b GIT binary patch literal 568 zcmV-80>}M{P)HXS^nF*8Ndp_JOBUy002PSO>*ScGoTz{90c&#sbi;!?W8Ya+es63xSV{7NibNY{wUO*qhinZ$y6B~=HtSq?QZYcKwh94q2d`?bI--(l_JyiRJ-$tf53JJ7DashV004wR_D^k(4;TOd08n(_5)ueyDt@Q{0000P-rjb7Tds6!no;hgN6!0R zE{ly`y!CCl|FzqDJ#We0v(8VOH|xA*;U){Y{`CjHa>ZU(pSkSjrEPD8Z%$a$oG9U+ zHEWxW2-igMjS)H0T^HJNSDIh@FUZ$k&fz3J$wOt1)4~Fk*}(}86U?ppHZNPYtnJcE zlf+H&L7bB$Zp{1hP@Xf#fql}+6pmn)`%Diiw;SZ2%5C8OAfq7oPDD|t^T#v>?(WYw z7TsZ$U^&tK>eZ_QD;sier*HPGmDqebNL2P?+)$&fn}Lh zxq@@_b~4Eo?JTScU@VCh@B4c*=bNIL#?KFb*Y7Vsld^f^M-9s88W{fQFJR}2oYX0V@lx>$eTvbfscf@z$;50%TbFS__* z-v?9Q=$T$i?UIVKTIv;VaJ+D^`}Zn3>%tbx($`n3ujj?bUSIyWYE5e7`VFS0)Bf}` zuD`PC{)Pu^?+zDiTN?MHyeU(3>eMgq)aU z>NPN)So(j#!Fdn&fBu;M<*WbxJBL83UN)P*U1r05VMQhPcT@Qdrmbuj6XRufmud6e zm;HP)*6zUPy*FoBJ+x=`II_Ypd1c;WwJ+sst*$IA4*$-vVDI_XD~1QyYpohiE!ix^ zwu8Z+!HOp?Ky0_z1vGuisWJeb%2ntIaumBa(4Bd2-lwJf^Mp4>b6c}9WskAx>L z3?CYCe_)fIV*X>Zqwxlw38%&H|9v5MEjH^%rQO5edmrL&h}Q`EMs3kj{iFMU(M&V_ z$NR>&4SQDHUm_WLdt3OA?24`a_ddL5jAmf{_;LFEfZ5+3i-h5^YH#_DS1}&w?N588$?p3e+?e;;2f;WukV|jfxh)!V76%9Oi zoPk5moB7j1E}8ZFWaGRACNeBv4a^k=K>~k_J_uVx)dDTN?2tWy#rDAM2S=34wy_^) m0%iH;KWo!Bt_CxaN`M4G6`vv-gH<2%K!TpGelF{r5}E)Zsr5$y literal 0 HcmV?d00001 diff --git a/main/ui/src/main/resources/img/bot/arm-r.png b/main/ui/src/main/resources/img/bot/arm-r.png new file mode 100644 index 0000000000000000000000000000000000000000..c30d8328f0a61b6dc09ebb0f5c86045bb3a2a51d GIT binary patch literal 572 zcmV-C0>k}@P)fS6qxYn|!Mm{olAuXlUqh*LCkZj`Pg-{rIr3uog;$kBW+_Zrk?TOP4M& z8OyT1#>U2WA}_%6yeo2W!!THUd_2p@$OyO#N|6m86BAQj)3jgVgZJd*WD#D_m#lz@ zh=|^D>to!D&@u}skN>8!fVjB0zX=#idJd2#Kr!}ZzJcnb1z;Eq6Z1fLc=&GucJAGG zzweOY1u5qj68Z(}zpm>nH8qumM?|n?tJg4u59n4vl?y|l?AXcEMhC$KqsLBooSvTE zT^R`oU1-4YQCDRNp+8rLwaQ4?ckob>2tRPdD69^-ljC<#R|x?Kdgb7eqq+0Ig-e<| zF`iOIz_iJ1Z>*2eGBG>4xkWq0gb>1IlOL~tl!WPVs*#? z^{7i|Wm@*Ni<-_hY}p#D2qL`T38+GOPtZsTYlAAF7#ImY^Z)xO3z)}4z6E9T)HaJ= zKv}>hG2Z`D8xB|iWdSpJ$d90Wp4x6+yG>a@ZSa)`O$8l5VP3QO|BciSM@s;cNaFu1 zf+3(3bpZvz6!0?tE>BxT3D6!?{Lvm92mk;800`QjdXXvs003aeD-sBb_qiwl0000< KMNUMnLSTYsl>Kl3 literal 0 HcmV?d00001 diff --git a/main/ui/src/main/resources/img/bot/arm-r@2x.png b/main/ui/src/main/resources/img/bot/arm-r@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cbcfdf03c963714ec6cf6a5bf9ae56899dd3b073 GIT binary patch literal 1122 zcmV-o1fBbdP)U- zv1<-Mk}Odajc?nwZ3Mn;8*AIPZQE`yx~e&Dt*y}7_S?Je$j$1mnPBF;7jecJxc3yY zrgnI}-aYuddEUHvVZ$(f<%BdI6yMY1@f>qF9At&h=i6qQ<|mw%Mw3dVF7f;Q32EsP zoKEKn!!YV|a+-{Ru~R%Q2NVv6sj#q+5{bkfi1Q4mr}&`IUvNZcJK`x4i4Y1GDFByn ziV6>I?$%{0-{o?hKH^cd6<>0SiVljsJ=@WrpPw%lE;62zRCI7!uav2L(=?@}3oKPP z0p9lY$s|B57PBM3E>2Qi0eWTF?~6sV02=N9{<0FlFbul_EaW5=9qi?A#9h8rfM76a zSAc;EN5JFv`F2+zbwpJpf83la}R2 zqEV_=r(Qo!Pm?iCbE5cA2xkXr`N2?#D%Y$-zx`WgAGk}C2zWxF(7)LJ?K~%d+wCUY zD5jtCY51to^l$k}gyPT9TeBE=umkuWH;1!uAFv#s6N873ptha5(0>)H5DFJL2=381 z4jvRd1&|fZ+q6qzz6Y1gCTLd_?cb_umB@)ohO_L zE-Js@puy7D#q>&m1#p1VZwU1rFo+s8Z%M7%cckevXJvX^{JA~}umA%96_Yr>G^&pP z-vBBBAM23-3vd}&RssQraC-Vk@Vm{@HQf@j0GEQL#T3BH$>}A*&w%VQZN^L|wu*m) z7r|wmp!xz_0+PVS8$&TFU$sX1GlOlQGq{bDR9k|#!I5kNbnMzay?jxGuQ^Hew}pFT z#pf3kP@9gO#nMHNgFiV*wI#UE=V|l=S0;L-~m7aLv5}##I>BLdICHw5umTlwO$rLeF1Kk z2w>V+cL`W0tlObr1U!-=kAr8jta)D|j=^cFDL`LInMrU{hBfUZYieOG33>st;3x=y$I|}(3NRKN2b;lHoTAzSTm|v~WgskBcNwRr zj|4XXzfd~g<(D!EE(0~eHi-aJIib=c!ZqMGFccV|2e^@wIoTc@kN^Mx00{d3x*dTE o0000000000000000001g0q8JtInjJ(Jpcdz07*qoM6N<$g2(UbTmS$7 literal 0 HcmV?d00001 diff --git a/main/ui/src/main/resources/img/bot/body.png b/main/ui/src/main/resources/img/bot/body.png new file mode 100644 index 0000000000000000000000000000000000000000..a473413e6cb80b07b745692156fff124b910c5fc GIT binary patch literal 1418 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 literal 0 HcmV?d00001 diff --git a/main/ui/src/main/resources/img/bot/legs.png b/main/ui/src/main/resources/img/bot/legs.png new file mode 100644 index 0000000000000000000000000000000000000000..e53152382f9e7a45dc0c4af717978ea2ac7de5bd GIT binary patch literal 648 zcmV;30(bq1P)S8rf9kWXl z*RgHenfyC=Jaf)^^Ja3_`mXj``#<{|(<`1UHUv6QU@#BN0gW}t9ROB>A|p2mI{uI0 zTY;lMVi8T}Bwr*(-w%UUD&{&Psq80zG|tGa;2rR5Enp{*P0s4IfTrXn_?Ntad4uEnGvbb3HQW(N>={ZF)st zlif*qJoalPU;{7M6$n)mKZN~A>sma8qGr$~U# z%-&jNhf7j&VhSfS`&s=6e>5QyuuW|Ny@4dU0&oP3uCstt<|tEu_)0)*S6e_QkpP2? z0E^2*^&xf5jHYwDw9q3wQVe z(&QPsf0wBRDrNSFWeb<+aG0ty&;u85`17EZ)(=D(aEr5uFCeXQW*cw}{E*4~3Vv-B isZHQfFbYP&C;$MddQq(D(L?qC0000_;rDX0 zqjm{ECb7Wb{hQg$Jxe@Aj-THqJ2!Vr?(H2j$`9V;J6HbjW>;~xcG)%QbhW74(ZB!Q zlzY9pZt|=3YIC1#pX2qn{(bf3(BRnEoGtq5Mn_H`+0FKI+K_f}Xe6^BL~H&GaPOH?eTXk>K-@M=poW?s(4S{D1qb9k)`} zg_jm}vOKM6`=n(4>2UK!7VggC(%c4diAPETy^A{z+vINS^3XE=@?W~s+UHRv`^H8G zyC*V}Z~M!gQGMttEC0{*$h^Fz++VL%2n!-)?-K=*RbDmWjU2gTomN zS3X+Zm3koScm4QgR*`%!n}Z)NTz{uJ@6E)=!gGJNs*B9i)BpKo;?%>|yVm|?mPw4a z=sM)vefha~tMTjCY-esVALnhY61Ge`^Ga1djhQFOFIMWnu_>Zv+u2)Z3$|akH&I)5 z!_X_CPTBHdb5BgpE|UYZeJ(ESouVkTQ|Z{BH8(!~cG;Ief3+TC+tPmtxuu7f!Z zv46gFbsbimc5-3D-93BkR()`{pP9;afLn5BOyW1TiU!RNvq4#sTN zn=2UiA^FpSo6-l4PUTSzx^&ccx|T$A;FAci>8mbDK4i65TX)jvtMr11gEyur9T$7{ z+T3A3quRpz4E`IwGjP7NJ#gXn(M;Dq%`?{*vsIjW!&{&oF7$qdr&#$5j~gpbT7x5+1sS~hZ39h)EoB~xfC|LW##rRH^^I=bbRTi*;j4eS4sTvoz9kF z?0U4gto>r}frZuO>$S>iL_bT}^43fPg=J-|)B~$WD(=@_a!XYGxL6_-|->A=8y7< z#pmStGz-jH4n{PYMVdY2Yhm%b|Kfh1Q03%Z&psR|V7(*EqiFuA*u3uO&JQ0q*c37Z z`IHHlP2PR0Gj{WXssmGc&uN@_d&Iow^`qAvzfB7FidXLbBJ<+P%Ws$8BFc?zNBk00 bL8XR8fxxnDThF}A0dYKC{an^LB{Ts5y&fl1 literal 0 HcmV?d00001