From c8e94bf583adc38c36e968eb00f21a8b8ca70257 Mon Sep 17 00:00:00 2001 From: Trevor Johns Date: Mon, 17 Aug 2015 09:37:47 -0700 Subject: [PATCH] Sync mnc-dev sample prebuilts Syncing to //developers/samples/android commmit 2be5f5ca32. Change-Id: Ia08c63655bd122c7fbb0cc3a50eec95d8edcb3f1 --- .../res/drawable-hdpi/ic_launcher.png | Bin 4199 -> 2323 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 2535 -> 1551 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 6022 -> 3202 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 11040 -> 5061 bytes .../GestureListener.java | 96 +++++++- .../AndroidManifest.xml | 29 ++- .../res/drawable-hdpi/ic_launcher.png | Bin 4199 -> 2259 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 2535 -> 1540 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 6022 -> 3056 bytes .../res/drawable-xxhdpi/ic_launcher.png | Bin 11040 -> 4886 bytes .../res/values/strings.xml | 3 + .../AdvertiserFragment.java | 225 ++++++++---------- .../AdvertiserService.java | 223 +++++++++++++++++ .../MainActivity.java | 4 +- .../ScanResultAdapter.java | 6 +- .../ScannerFragment.java | 3 +- ...ngerprintAuthenticationDialogFragment.java | 15 +- .../browseable/RuntimePermissions/_index.jd | 7 +- .../res/values/base-strings.xml | 7 +- .../RuntimePermissions/res/values/strings.xml | 3 +- .../MainActivity.java | 175 ++++++++++---- .../PermissionUtil.java | 45 +--- .../RuntimePermissionsFragment.java | 11 +- .../common.activities/SampleActivityBase.java | 53 +++++ .../RuntimePermissionsBasic/_index.jd | 1 - .../res/layout/activity_main.xml | 37 +-- .../res/values/base-strings.xml | 1 - .../MainActivity.java | 113 +++++---- 28 files changed, 734 insertions(+), 323 deletions(-) create mode 100644 samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java create mode 100644 samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java diff --git a/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-hdpi/ic_launcher.png index b1efaf4b23929755615dc16fa196c5f8496bbf9a..4252db138012406b581914121490f29a23df6383 100644 GIT binary patch delta 2313 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 diff --git a/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-mdpi/ic_launcher.png index f5f9244f24dccccab7dbc1259ddfceb0cba619c0..969456ee0176d88f5c54924834233f4ef1340cc7 100644 GIT binary patch delta 1534 zcmVGiF{@=P)?8U8 zw^mLaIV~l@at%tH{FyCkV4E(GEL@5yTEj9a*DyK$*ufnL+%*lCi6+q`H2f2H$8mRX z_rAZL=Xu}z1A_Oy$I-CyY@gk6_wIQ<-}m|RKF1RfFjc0?RDYSU^4YPGXpTDns z^Nvps(HQ*xe)A8?Ejjpm-X~uG2EWIj{b#L1`|>c*FU|&C!V%CfJPP{w5yUaHVPfyX z(wuNYU$xrdW|hODjXwdcvZHRKEb~Y3@NillIs$FdY-nE;#!ddifFNj@uZFX8fgy%v z4$EwY=2;_{$$v7#O;a?>gDlfoqTFaDh)542Si}q(5rbBu#%^8%7;0ueG3n$r;RJ&! z^l`5Nx__9xVlv8uCJeg24_o0iKxgGFOMqx4 zzcD`pZ0#H1PQ3zXdp`+a)UAQrKT6yo{2&jFLY@J5PFQIRU`fw#p@M(yc~;Iz8rx9q6u|KFLF4h_3;>$m44) zSBamkr+?BVKX5Sz2~(^%dZmV3Ifl-!`J#ePc- z#-HM#PZbSAf1hFk-1rVG_59?aQNT9<$A?t!H_R$@<0O$cHZ4UfshJVISxdzGPDg;L zDG7$IS27(kbO#2z%Yo`S1dawVCwZO$csyYZ&3{D7a=8%oz(Utj>M&jaL(Lp?_hV6z zI#-VjE2!5@HIXQUcX=NIaNHjBlrb*WZbT1rhX-$hQJnyWdMQwrKV1_X-+877{vkau zcDcu8u#W##i;U_ zff@l&>Tv~-D1F@$GaO32zRgJ2uh=I{bqQ_j$QL`pe1 zd(=$}=1VDcvS?ni*8oYcq$polmwyhE5$yK=5AgI$YnD&e0NBlIfEd}LxzDfNWSDI7 z*a^FF`TuT6?2@F7OINS&dhXR!hbPO{z5$+${N7i*)r(!Q6L#YgAP^CWPy`1Hmjnxi zPhq5rgyb#xyY1F@2_ zI^vpX<2~~T%qc0;I!|1(i}xGn%H=WsI#9uNlAXR`~hMD+t$# z>&9(l6cknY`340%AP@+o&nQ=>Djs`s8Twm10Y`IUTsLk5w}spEcLR7LiP(u7H~|3y k*}6skn6y)6s&Fj-0(?817u!qEhX4Qo07*qoM6N<$f-$MvWB>pF delta 2526 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 literal 6022 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 ABme*a diff --git a/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicGestureDetect/res/drawable-xxhdpi/ic_launcher.png index 6ef21e1f4dfe8eeec63fde13a453812511646c83..23ffa9a083f19e2714b002e0e1ad3dfa952cb7e1 100644 GIT binary patch literal 5061 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 WIWCBq^UZpWNJ~Rcy-L+O?0*3FTdYC= literal 11040 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 WRh@=GnE09b3XqXd5U&x{5BMKGuL(l{ diff --git a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java index 2e2921d46..19b588a66 100644 --- a/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java +++ b/samples/browseable/BasicGestureDetect/src/com.example.android.basicgesturedetect/GestureListener.java @@ -16,6 +16,8 @@ package com.example.android.basicgesturedetect; +import android.annotation.TargetApi; +import android.os.Build; import android.view.GestureDetector; import android.view.MotionEvent; @@ -29,7 +31,7 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { // Up motion completing a single tap occurred. - Log.i(TAG, "Single Tap Up"); + Log.i(TAG, "Single Tap Up" + getTouchType(e)); return false; } @@ -37,14 +39,14 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener { public void onLongPress(MotionEvent e) { // Touch has been long enough to indicate a long press. // Does not indicate motion is complete yet (no up event necessarily) - Log.i(TAG, "Long Press"); + Log.i(TAG, "Long Press" + getTouchType(e)); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // User attempted to scroll - Log.i(TAG, "Scroll"); + Log.i(TAG, "Scroll" + getTouchType(e1)); return false; } @@ -52,27 +54,27 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // Fling event occurred. Notification of this one happens after an "up" event. - Log.i(TAG, "Fling"); + Log.i(TAG, "Fling" + getTouchType(e1)); return false; } @Override public void onShowPress(MotionEvent e) { // User performed a down event, and hasn't moved yet. - Log.i(TAG, "Show Press"); + Log.i(TAG, "Show Press" + getTouchType(e)); } @Override public boolean onDown(MotionEvent e) { // "Down" event - User touched the screen. - Log.i(TAG, "Down"); + Log.i(TAG, "Down" + getTouchType(e)); return false; } @Override public boolean onDoubleTap(MotionEvent e) { // User tapped the screen twice. - Log.i(TAG, "Double tap"); + Log.i(TAG, "Double tap" + getTouchType(e)); return false; } @@ -81,7 +83,7 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener { // Since double-tap is actually several events which are considered one aggregate // gesture, there's a separate callback for an individual event within the doubletap // occurring. This occurs for down, up, and move. - Log.i(TAG, "Event within double tap"); + Log.i(TAG, "Event within double tap" + getTouchType(e)); return false; } @@ -89,8 +91,84 @@ public class GestureListener extends GestureDetector.SimpleOnGestureListener { public boolean onSingleTapConfirmed(MotionEvent e) { // A confirmed single-tap event has occurred. Only called when the detector has // determined that the first tap stands alone, and is not part of a double tap. - Log.i(TAG, "Single tap confirmed"); + Log.i(TAG, "Single tap confirmed" + getTouchType(e)); return false; } // END_INCLUDE(init_gestureListener) + + + /** + * Returns a human-readable string describing the type of touch that triggered a MotionEvent. + */ + + private static String getTouchType(MotionEvent e){ + + String touchTypeDescription = " "; + int touchType = e.getToolType(0); + + switch (touchType) { + case MotionEvent.TOOL_TYPE_FINGER: + touchTypeDescription += "(finger)"; + break; + case MotionEvent.TOOL_TYPE_STYLUS: + touchTypeDescription += "(stylus, "; + //Get some additional information about the stylus touch + float stylusPressure = e.getPressure(); + touchTypeDescription += "pressure: " + stylusPressure; + + if(Build.VERSION.SDK_INT >= 21) { + touchTypeDescription += ", buttons pressed: " + getButtonsPressed(e); + } + + touchTypeDescription += ")"; + break; + case MotionEvent.TOOL_TYPE_ERASER: + touchTypeDescription += "(eraser)"; + break; + case MotionEvent.TOOL_TYPE_MOUSE: + touchTypeDescription += "(mouse)"; + break; + default: + touchTypeDescription += "(unknown tool)"; + break; + } + + return touchTypeDescription; + } + + /** + * Returns a human-readable string listing all the stylus buttons that were pressed when the + * input MotionEvent occurred. + */ + @TargetApi(21) + private static String getButtonsPressed(MotionEvent e){ + String buttons = ""; + + if(e.isButtonPressed(MotionEvent.BUTTON_PRIMARY)){ + buttons += " primary"; + } + + if(e.isButtonPressed(MotionEvent.BUTTON_SECONDARY)){ + buttons += " secondary"; + } + + if(e.isButtonPressed(MotionEvent.BUTTON_TERTIARY)){ + buttons += " tertiary"; + } + + if(e.isButtonPressed(MotionEvent.BUTTON_BACK)){ + buttons += " back"; + } + + if(e.isButtonPressed(MotionEvent.BUTTON_FORWARD)){ + buttons += " forward"; + } + + if (buttons.equals("")){ + buttons = "none"; + } + + return buttons; + } + } diff --git a/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml index 48084fc97..cd2a65e59 100644 --- a/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml +++ b/samples/browseable/BluetoothAdvertisements/AndroidManifest.xml @@ -1,4 +1,4 @@ - + + + diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-hdpi/ic_launcher.png index b1efaf4b23929755615dc16fa196c5f8496bbf9a..48d6465445cf7dfd7d9d70ee54e4512f46315d62 100644 GIT binary patch delta 2248 zcmV;(2siiVAkz_$BYy~*Nklp2#R9xP*{{z6_V|*?o!q;kilFthHS+ktVqqX1eSYP(Zt@Vc`AF+L%9P{c7^xVj#2ynThRS3r(MQG6Q^EbsJn5`mOxh?LD5>6YB7uXs9nSRI;P?U~ z#WDJbGa8H(hlbHfVm#`om4%aFw#~oOaiT12B1;?Z^MBW8;$4+Znd1S60Xp22iEZ`= zXwvk<*G%IAha}>?YbI>7e?X<*{BbM#?(&F4I>7)bB+i&@V z^I3C0GZ!UR+X#{s5-sh*wu|c8AgZjH4e3d;aFhUPdE%K^=YR_mq^kcq1)O*ehc9Uj zZOo9;ms`Q*X6LPCr|m3GxeX#Q9Fku+YJUJ}aaMj$y%WB^=@A2c=LAC~3pDMB70%g) zfl>vaW&!8{1mbbHM40XV;V1>t0_WZag45|4b%-Gn&N-#c7-Bfz++E!wY@1x$ zCdMgmVS`9DKH|s)G7=yyaAu!!!qslin3HrYS;275L8Ov@IT#$w%1`XfHa&wwpntFM zg2N@;+~FIJ8bDg%AjEe)>^y|?jdYoht!QSKat;UQo1!bd%>nMcyp;_>3ey@$ zOB|e#|I@_+9VI{iyt*0w**<)rSlieKv6aI(hDsaQxC6zf&vo7&ha?Q+3zA$Qt#Iho zN&x!a#SE!mf7~uQ)kr^cQ&`GlDt}wxWK$nBI^4|TF>xxmjD@5IkXkrL2u>`2bSwc{ zQr|{SR)^2%cb@MaKF;BW6T&PxW%nJ&7bK-XNwer!x?kV z84ahRjm>CCN`Tb3`jAAB-nir#frCpq4@7@prU?$8BRACniX}j*UYfD=Kz}q@m1rEC zbEH6d7kaeAA?IvNq)1AERJ(3f!y@rGm~S~C&3g`y!>6iK9U#>&>JcP7T|gWi2NZA| za-_|KN0JMq*%eQM#2ow)pxs~fK-QO?fybed)BsZbq8=hu*o1??py1%DhAFwI3`nyp zo&*U|f{LRQNb}3Vh*Z%MJbxS|Kmof}PLLuwq=4h#k}nJBuUA;0W8?sjO3cyWOr{bo ziQY0Cog`6IB5SyO)Xk!l*_ebgIKv5}GKop6I0Q!&f+LPy9D*}RF_5JjVl7=lV))c_ z!DLCIsp#o^)CD>!QB)%NRChrHrs^g{d}xJ;!#5zD{L3ed$|NDp;(riSQ3S}bKuIqv z`O1HwMOnMoF447Lf|hHb>Q%DG)5 zjARDMqMyy4n>p_nOLr&#GBsbC$JcFYeB$|Bp!56ZHuol{jNDeUkbR}E&*_!c<1y2Q zMsM3=Td+;oHm;3fa&P#-8`|PXW|83iRmpVbBrizaRIqPw!+$G<@Z3vp3i-pPoekvm z1=8&`%mUN){D$?r$^P>Sgk|>8LCc3tOM2s>x6Z~ zI&w>S!0UtuSNEl?c;uN?Pd>YA&isX|@IGLYVE(rfxbGya3)TtihIN#CAgFj1<9Z9O zSaV5mXhG?N)O+Y&r041~m#dpGD65#{7%7sQcY=vS*C~vvQ}|D$PN5Pip%N;x%Krej W73cG1DF4d<0000HpaK$X7h0?J& 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 diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-mdpi/ic_launcher.png index f5f9244f24dccccab7dbc1259ddfceb0cba619c0..42b3b1edf1134199220bb036deccd1ddf8bf5e6a 100644 GIT binary patch delta 1523 zcmV;AK&ulx~0QyZ;W$qkmWqy~$c;XF$sC1DKaQx??s zYtyRQZE2P5w&j%P>iV^Fbr!XYdW$-a5<3RbZpz|ygITG>iJSfB5EOgX+BB!m0!U=t zAj9l4EVC#xDSuC&W|={m?m@&UmZvD;l(1Si(+Hs+$h9moij+cL+k|dj{i7=Nizb`A zq=rTOz5Ye70ke*Gu9}SUA%x)4&Q;z6N;@+Al}~A~z&xErPyaKz@IZ;baMo5 zne9Ms0)ol&3h;N@U;qv#;=C~OV!i=<aF*v4;LEKtmqxVr+o0k$8<5+2A0F5p z@b{n%Qh!eyL0*0rSqE%%8d|0BgU2I8{q3F?D{K1@Q@b#-hVp+an*X5p}7ke4a3a3$SMQGR=Wd2 zFo22Z0x^jgSDsIRukxE(hGE1GkBs)d5r{o$fSDRSoVaNLDVajc&i63eUT^HB0Y(rb zBqI3az}WI!1Hx$lU-?WTkGD*m^R>6FjNl=12Xc5spuD6^4|#27<{}Q)7~S%OU^NLS z?|%jniSR4HS9xa6lMD9iYXeL`qSgpUuCo_xlYSVaL>}k7=9U#ou3JXS6GY@)5LJv5 z!8d@5{E8+kBmXCnPp&n9Tz$tC@bTq-u#gL)d&mxp>buzsmJmkHeB{ZlvE`jq^o)++ z6TtN=F|Jc}47ubz1IS0fDEUX`{CM&>jeig(!puK__xE}HUZd$^Dxo( zU#jo%P2L%xx#y1H6X4U2k%}%?fL~|cDenx>^s*6L1N{0WMIxTB>xSf0T>{BF=>yTh zYup3;`XyD-EwFqqi}E*a0fPJt)bt1|Uy1JkhX6SS)S8Jj%QQr5CKsw3IYm~(B7dbw zC?QC-8N@_JTxOv1EFww}Ll+B?cR~Kf%{>-p1ebuAJY7co;Z}QmNvl21gIEvZihp&) zQeui*9MQ$CcH|ikqL4!^_9#`eeU6esmLokN>l|~GmmFk0LuBDE_D8wQQiNQwPv3tg z-D|+ocUEVtSi2b}Bk1=34|w&h_kUMT)&O+#8W5e38uR9wP4_38JUXEpm;c_CCM{mN z;kEbH-Cq8|Mw=&TYd!)`9^dXO-u8F()7&h`NJvPR5b+^rz$bZyHC*Q- z$05ai;=Z$0$6(JlM~3j2LTo;Dl5SHdb<6>jN3CzK&s-(&Gp``rC+-`Mu}x8+6ZixL zh$Irp{AC&MZcKUM4u`ad{7oxuPA002ovPDHLkV1lK^;o$%P delta 2526 zcmV<42_g1`4CfP&BYz35Nkl20!CrdB9ADN@+uv% zouD%gipW@PjS%@m90#h+@%No`e!FKc`wFx*y_1uB&tvcXzTf-YYZD*y!+-WM$1j)w&dJnLs3lEs zQlo_LHnkyYV@x3J_1Bszrxz4%w%hHliA*362n=_+yI%OxnlJ7koP$gtx2LDaZL`@N z6B#^X8yg$*=jP_-Q@@7^O+sSFhNTB$Y5#KKNe8uKtKfO zJc={TjnY4ik5J!F*M7PK#^g2T=?1vIvbk&-Bv0)MwE=#VojOixS%oj3b|*=23NV0! zKoYHR5HhDjgQjZ`pe~WX9GyWN%y0lPADR&W1OSAl0)I5;r+*;er!kNjp#fZv(l`iU zJqUrws3z&ISPawYr8Yt>V3pP6qF3MUWdhIyh{&c%frRRZv?R4feP9O(oXs193ZxA1 zI0Ga|n9`Nfmf%BR)pht-=M!_Y=yrWU*;sTv`BNZZ01hVs@CvLnEs)YuXAMXa1Rp{N z34n((V1GDFg279((2`}z2LVR_4?-PxOTfwgph2@{So$EP%!xG-5{&|`NWg5A214Lj z(oALq;O~~er&$AWw7`;AXQh>(A4usdEhy*JcF(3G9WvyYO6saFfky$4`Sx$dF`;`U zVD*KFwr>!?`WYE6(bnsm7zAV_N>Bq>w{3o&cz^Kz6!Gj?w`jdiB<@uM(VX~$_5t|N zkLTrz`B`b=?LMFQ$=S=|=7?VsxF;X@D54O|2H@ur_zNBa-*Q>>OYrgiAZ`@;u=4;C z-ImYfijpY={)SIfy?I4=hn3_%kUa3o-?iyH5Iu=mrz~7M%q9?PiczIwZ2$+dVXI(Syo5{-x9!`t|X4XR>03X^iS4sZgp4+18pUUPV zB{egQa&BOfaS6=$tLQheL&XH{vt@3MSeTtIE_wm}is&b^ z;e;k@AD!H|lt7umdBG&n{jGViONr?qV1KE5*kk}?a&nURX=%PV($z2i+U*Umfi!q% zfe%RjX_EY+oOJOXNxoXz{Ah%l$jHo5ObVpj-;P6I+HcO=*EhCV;gd(dm`IgZ3}M+r%@Jq#n+lL}oK2o@;T- zPzm7h30YFhGFpMhHA-exp<0SOWBlrq^JAOPR1mOKnRhy^BC&f>zDOfY{PLY^;!hnO zY4xZ^v5$CeL7rHWH%a`5Z2nmS?|&hiM_Hjt>+s4ns12jcW~XIeU}wMfR2%|%4gcze zB-xR5Zi>NfPe-Vs`d4DF~ZIZBo~k-ULZ|;!lhnn zAlhs`{a@jsB*oxO5^8S{Zc64(Es{ww!5igq2uyyZRhp2wO*CtLXeX>>)}a$c*^7${ z#8uy@_`%7pAb1H}Z;z!GG=FflOAFs-hV9t2pIA-I8;?NduUn-PTS;&~6f(p2zZfs3 zviSP#QPE4yx+i8LK>`s>DY)T}0v_`>zgrU9gfjM@lX-wQ=q8z1;JJKY;PjGAnvVjm zRKWY=L`eigt+sz?=+^o;1kzsyfsk|~fwqpB;2C&i4Jap85y|j@2!H-y^${lH+wj@g z8c5yurt|@;22`d+ox@o-bREpTx{!mkBNEWs2)(IL+F_?QudU|9xnrmz4uRx45QwY+ z6TDe!&4 ztZUl5DfLJi2>C(CCx5~c8+FErxqR(tn3-vEqd#1cP4~f@Ie8O)4gz?_%9ShU{C&mu z-v~l4UYKJ^v+2+)`QTX4bE8}YLn(nY{ecr5l9?H913JlZ5`6sR-W5lV9C?dw zf0LS@3E;y(+{;OP4-R;D0^U)zx3Vbm`JXVAK#EIDGi@9i*9v;9xcYu3PXEQ-Z$@F0+@GmQLTbYu8zl{*@OrT!$Gf#`D9p z{rmU-e9oLX-*da&m)5OYw`yo;s6$JI?-Pvx=3PX5#=(OJUneswI)DEB-u3I(Z>Di+ zX5%^ThJa(kh7AkL%gc|`2RCO!L&L_py1Lisp9z97<8;J14cm(j1bjm$TDF7U% zh?HDgTl+*~W8)tzrWljVcr|d}s#U9+Q&Li9QGXb0sIRZzMt4Uzi{yOMDf)C$2o=YU z9W$NS1cVYRhs<%e&*yuxySw`$MWsbF=1Eiq<0X)_eEIUXEdT%j diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xhdpi/ic_launcher.png index 5d07b3f06606f5f005afa8237a53f7ff3ef5966e..13663e8be7857c820bd759314ac0ba13fec01579 100644 GIT binary patch delta 3051 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 literal 6022 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 ABme*a diff --git a/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BluetoothAdvertisements/res/drawable-xxhdpi/ic_launcher.png index 6ef21e1f4dfe8eeec63fde13a453812511646c83..5e84f3a58b81d30dfbdcd39ebb36f6c4744b6eca 100644 GIT binary patch literal 4886 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 WRh@=GnE09b3XqXd5U&x{5BMKGuL(l{ diff --git a/samples/browseable/BluetoothAdvertisements/res/values/strings.xml b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml index 197178d00..927f3b603 100644 --- a/samples/browseable/BluetoothAdvertisements/res/values/strings.xml +++ b/samples/browseable/BluetoothAdvertisements/res/values/strings.xml @@ -26,5 +26,8 @@ seconds. Scanning for Scanning already started. + (no name) + unknown error + Advertising stopped due to timeout. \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java index f3645fc29..c97b90435 100644 --- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserFragment.java @@ -16,11 +16,11 @@ package com.example.android.bluetoothadvertisements; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.AdvertiseCallback; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertiseSettings; -import android.bluetooth.le.BluetoothLeAdvertiser; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -32,67 +32,121 @@ import android.widget.Toast; /** * Allows user to start & stop Bluetooth LE Advertising of their device. */ -public class AdvertiserFragment extends Fragment { - - private BluetoothAdapter mBluetoothAdapter; - - private BluetoothLeAdvertiser mBluetoothLeAdvertiser; - - private AdvertiseCallback mAdvertiseCallback; +public class AdvertiserFragment extends Fragment implements View.OnClickListener { + /** + * Lets user toggle BLE Advertising. + */ private Switch mSwitch; /** - * Must be called after object creation by MainActivity. - * - * @param btAdapter the local BluetoothAdapter + * Listens for notifications that the {@code AdvertiserService} has failed to start advertising. + * This Receiver deals with Fragment UI elements and only needs to be active when the Fragment + * is on-screen, so it's defined and registered in code instead of the Manifest. */ - public void setBluetoothAdapter(BluetoothAdapter btAdapter) { - this.mBluetoothAdapter = btAdapter; - mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); - } + private BroadcastReceiver advertisingFailureReceiver; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setRetainInstance(true); + + advertisingFailureReceiver = new BroadcastReceiver() { + + /** + * Receives Advertising error codes from {@code AdvertiserService} and displays error messages + * to the user. Sets the advertising toggle to 'false.' + */ + @Override + public void onReceive(Context context, Intent intent) { + + int errorCode = intent.getIntExtra(AdvertiserService.ADVERTISING_FAILED_EXTRA_CODE, -1); + + mSwitch.setChecked(false); + + String errorMessage = getString(R.string.start_error_prefix); + switch (errorCode) { + case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED: + errorMessage += " " + getString(R.string.start_error_already_started); + break; + case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE: + errorMessage += " " + getString(R.string.start_error_too_large); + break; + case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED: + errorMessage += " " + getString(R.string.start_error_unsupported); + break; + case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR: + errorMessage += " " + getString(R.string.start_error_internal); + break; + case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS: + errorMessage += " " + getString(R.string.start_error_too_many); + break; + case AdvertiserService.ADVERTISING_TIMED_OUT: + errorMessage = " " + getString(R.string.advertising_timedout); + break; + default: + errorMessage += " " + getString(R.string.start_error_unknown); + } + + Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show(); + } + }; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_advertiser, container, false); mSwitch = (Switch) view.findViewById(R.id.advertise_switch); - mSwitch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onSwitchClicked(v); - } - }); + mSwitch.setOnClickListener(this); return view; } + /** + * When app comes on screen, check if BLE Advertisements are running, set switch accordingly, + * and register the Receiver to be notified if Advertising fails. + */ @Override - public void onStop() { - super.onStop(); + public void onResume() { + super.onResume(); - if(mAdvertiseCallback != null){ - stopAdvertising(); + if (AdvertiserService.running) { + mSwitch.setChecked(true); + } else { + mSwitch.setChecked(false); } + + IntentFilter failureFilter = new IntentFilter(AdvertiserService.ADVERTISING_FAILED); + getActivity().registerReceiver(advertisingFailureReceiver, failureFilter); + + } + + /** + * When app goes off screen, unregister the Advertising failure Receiver to stop memory leaks. + * (and because the app doesn't care if Advertising fails while the UI isn't active) + */ + @Override + public void onPause() { + super.onPause(); + getActivity().unregisterReceiver(advertisingFailureReceiver); + } + + /** + * Returns Intent addressed to the {@code AdvertiserService} class. + */ + private static Intent getServiceIntent(Context c) { + return new Intent(c, AdvertiserService.class); } /** * Called when switch is toggled - starts or stops advertising. - * - * @param view is the Switch View object */ - public void onSwitchClicked(View view) { - + @Override + public void onClick(View v) { // Is the toggle on? - boolean on = ((Switch) view).isChecked(); + boolean on = ((Switch) v).isChecked(); if (on) { startAdvertising(); @@ -102,105 +156,20 @@ public class AdvertiserFragment extends Fragment { } /** - * Starts BLE Advertising. + * Starts BLE Advertising by starting {@code AdvertiserService}. */ private void startAdvertising() { - - mAdvertiseCallback = new SampleAdvertiseCallback(); - - if (mBluetoothLeAdvertiser != null) { - mBluetoothLeAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(), - mAdvertiseCallback); - } else { - mSwitch.setChecked(false); - Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show(); - } + Context c = getActivity(); + c.startService(getServiceIntent(c)); } /** - * Stops BLE Advertising. + * Stops BLE Advertising by stopping {@code AdvertiserService}. */ private void stopAdvertising() { - - if (mBluetoothLeAdvertiser != null) { - - mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); - mAdvertiseCallback = null; - - } else { - mSwitch.setChecked(false); - Toast.makeText(getActivity(), getString(R.string.bt_null), Toast.LENGTH_LONG).show(); - } + Context c = getActivity(); + c.stopService(getServiceIntent(c)); + mSwitch.setChecked(false); } - /** - * Returns an AdvertiseData object which includes the Service UUID and Device Name. - */ - private AdvertiseData buildAdvertiseData() { - - // Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements. - // This includes everything put into AdvertiseData including UUIDs, device info, & - // arbitrary service or manufacturer data. - // Attempting to send packets over this limit will result in a failure with error code - // AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the - // onStartFailure() method of an AdvertiseCallback implementation. - - AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); - dataBuilder.addServiceUuid(Constants.Service_UUID); - dataBuilder.setIncludeDeviceName(true); - - return dataBuilder.build(); - } - - /** - * Returns an AdvertiseSettings object set to use low power (to help preserve battery life). - */ - private AdvertiseSettings buildAdvertiseSettings() { - AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder(); - settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER); - - return settingsBuilder.build(); - } - - /** - * Custom callback after Advertising succeeds or fails to start. - */ - private class SampleAdvertiseCallback extends AdvertiseCallback { - - @Override - public void onStartFailure(int errorCode) { - super.onStartFailure(errorCode); - - mSwitch.setChecked(false); - - String errorMessage = getString(R.string.start_error_prefix); - switch (errorCode) { - case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED: - errorMessage += " " + getString(R.string.start_error_already_started); - break; - case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE: - errorMessage += " " + getString(R.string.start_error_too_large); - break; - case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED: - errorMessage += " " + getString(R.string.start_error_unsupported); - break; - case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR: - errorMessage += " " + getString(R.string.start_error_internal); - break; - case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS: - errorMessage += " " + getString(R.string.start_error_too_many); - break; - } - - Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_LONG).show(); - - } - - @Override - public void onStartSuccess(AdvertiseSettings settingsInEffect) { - super.onStartSuccess(settingsInEffect); - // Don't need to do anything here, advertising successfully started. - } - } - -} +} \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java new file mode 100644 index 000000000..0cc3ff03e --- /dev/null +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/AdvertiserService.java @@ -0,0 +1,223 @@ +package com.example.android.bluetoothadvertisements; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.AdvertiseCallback; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.util.Log; +import android.widget.Toast; + +import java.util.concurrent.TimeUnit; + +/** + * Manages BLE Advertising independent of the main app. + * If the app goes off screen (or gets killed completely) advertising can continue because this + * Service is maintaining the necessary Callback in memory. + */ +public class AdvertiserService extends Service { + + private static final String TAG = AdvertiserService.class.getSimpleName(); + + /** + * A global variable to let AdvertiserFragment check if the Service is running without needing + * to start or bind to it. + * This is the best practice method as defined here: + * https://groups.google.com/forum/#!topic/android-developers/jEvXMWgbgzE + */ + public static boolean running = false; + + public static final String ADVERTISING_FAILED = + "com.example.android.bluetoothadvertisements.advertising_failed"; + + public static final String ADVERTISING_FAILED_EXTRA_CODE = "failureCode"; + + public static final int ADVERTISING_TIMED_OUT = 6; + + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + + private AdvertiseCallback mAdvertiseCallback; + + private Handler mHandler; + + private Runnable timeoutRunnable; + + /** + * Length of time to allow advertising before automatically shutting off. (10 minutes) + */ + private long TIMEOUT = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES); + + @Override + public void onCreate() { + running = true; + initialize(); + startAdvertising(); + setTimeout(); + super.onCreate(); + } + + @Override + public void onDestroy() { + /** + * Note that onDestroy is not guaranteed to be called quickly or at all. Services exist at + * the whim of the system, and onDestroy can be delayed or skipped entirely if memory need + * is critical. + */ + running = false; + stopAdvertising(); + mHandler.removeCallbacks(timeoutRunnable); + super.onDestroy(); + } + + /** + * Required for extending service, but this will be a Started Service only, so no need for + * binding. + */ + @Override + public IBinder onBind(Intent intent) { + return null; + } + + /** + * Get references to system Bluetooth objects if we don't have them already. + */ + private void initialize() { + if (mBluetoothLeAdvertiser == null) { + BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + if (mBluetoothManager != null) { + BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter(); + if (mBluetoothAdapter != null) { + mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); + } else { + Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show(); + } + } else { + Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show(); + } + } + + } + + /** + * Starts a delayed Runnable that will cause the BLE Advertising to timeout and stop after a + * set amount of time. + */ + private void setTimeout(){ + mHandler = new Handler(); + timeoutRunnable = new Runnable() { + @Override + public void run() { + Log.d(TAG, "AdvertiserService has reached timeout of "+TIMEOUT+" milliseconds, stopping advertising."); + sendFailureIntent(ADVERTISING_TIMED_OUT); + stopSelf(); + } + }; + mHandler.postDelayed(timeoutRunnable, TIMEOUT); + } + + /** + * Starts BLE Advertising. + */ + private void startAdvertising() { + Log.d(TAG, "Service: Starting Advertising"); + + if (mAdvertiseCallback == null) { + AdvertiseSettings settings = buildAdvertiseSettings(); + AdvertiseData data = buildAdvertiseData(); + mAdvertiseCallback = new SampleAdvertiseCallback(); + + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.startAdvertising(settings, data, + mAdvertiseCallback); + } + } + } + + /** + * Stops BLE Advertising. + */ + private void stopAdvertising() { + Log.d(TAG, "Service: Stopping Advertising"); + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); + mAdvertiseCallback = null; + } + } + + /** + * Returns an AdvertiseData object which includes the Service UUID and Device Name. + */ + private AdvertiseData buildAdvertiseData() { + + /** + * Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements. + * This includes everything put into AdvertiseData including UUIDs, device info, & + * arbitrary service or manufacturer data. + * Attempting to send packets over this limit will result in a failure with error code + * AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the + * onStartFailure() method of an AdvertiseCallback implementation. + */ + + AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); + dataBuilder.addServiceUuid(Constants.Service_UUID); + dataBuilder.setIncludeDeviceName(true); + + /* For example - this will cause advertising to fail (exceeds size limit) */ + //String failureData = "asdghkajsghalkxcjhfa;sghtalksjcfhalskfjhasldkjfhdskf"; + //dataBuilder.addServiceData(Constants.Service_UUID, failureData.getBytes()); + + return dataBuilder.build(); + } + + /** + * Returns an AdvertiseSettings object set to use low power (to help preserve battery life) + * and disable the built-in timeout since this code uses its own timeout runnable. + */ + private AdvertiseSettings buildAdvertiseSettings() { + AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder(); + settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER); + settingsBuilder.setTimeout(0); + return settingsBuilder.build(); + } + + /** + * Custom callback after Advertising succeeds or fails to start. Broadcasts the error code + * in an Intent to be picked up by AdvertiserFragment and stops this Service. + */ + private class SampleAdvertiseCallback extends AdvertiseCallback { + + @Override + public void onStartFailure(int errorCode) { + super.onStartFailure(errorCode); + + Log.d(TAG, "Advertising failed"); + sendFailureIntent(errorCode); + stopSelf(); + + } + + @Override + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + super.onStartSuccess(settingsInEffect); + Log.d(TAG, "Advertising successfully started"); + } + } + + /** + * Builds and sends a broadcast intent indicating Advertising has failed. Includes the error + * code as an extra. This is intended to be picked up by the {@code AdvertiserFragment}. + */ + private void sendFailureIntent(int errorCode){ + Intent failureIntent = new Intent(); + failureIntent.setAction(ADVERTISING_FAILED); + failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE, errorCode); + sendBroadcast(failureIntent); + } + +} \ No newline at end of file diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java index 871935d9d..7ea389160 100644 --- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/MainActivity.java @@ -39,7 +39,7 @@ public class MainActivity extends FragmentActivity { setContentView(R.layout.activity_main); setTitle(R.string.activity_main_title); - if (savedInstanceState == null ) { + if (savedInstanceState == null) { mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)) .getAdapter(); @@ -112,11 +112,11 @@ public class MainActivity extends FragmentActivity { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); ScannerFragment scannerFragment = new ScannerFragment(); + // Fragments can't access system services directly, so pass it the BluetoothAdapter scannerFragment.setBluetoothAdapter(mBluetoothAdapter); transaction.replace(R.id.scanner_fragment_container, scannerFragment); AdvertiserFragment advertiserFragment = new AdvertiserFragment(); - advertiserFragment.setBluetoothAdapter(mBluetoothAdapter); transaction.replace(R.id.advertiser_fragment_container, advertiserFragment); transaction.commit(); diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java index f3c141d36..5a9b95483 100644 --- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScanResultAdapter.java @@ -75,7 +75,11 @@ public class ScanResultAdapter extends BaseAdapter { ScanResult scanResult = mArrayList.get(position); - deviceNameView.setText(scanResult.getDevice().getName()); + String name = scanResult.getDevice().getName(); + if (name == null) { + name = mContext.getResources().getString(R.string.no_name); + } + deviceNameView.setText(name); deviceAddressView.setText(scanResult.getDevice().getAddress()); lastSeenView.setText(getTimeSinceString(mContext, scanResult.getTimestampNanos())); diff --git a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java index ebb1ad085..4f5c2aa59 100644 --- a/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java +++ b/samples/browseable/BluetoothAdvertisements/src/com.example.android.bluetoothadvertisements/ScannerFragment.java @@ -83,7 +83,7 @@ public class ScannerFragment extends ListFragment { // We could get a LayoutInflater from the ApplicationContext but it messes with the // default theme, so generate it from getActivity() and pass it in separately. mAdapter = new ScanResultAdapter(getActivity().getApplicationContext(), - LayoutInflater.from(getActivity())); + LayoutInflater.from(getActivity())); mHandler = new Handler(); } @@ -180,6 +180,7 @@ public class ScannerFragment extends ListFragment { List scanFilters = new ArrayList<>(); ScanFilter.Builder builder = new ScanFilter.Builder(); + // Comment out the below line to see all BLE devices around you builder.setServiceUuid(Constants.Service_UUID); scanFilters.add(builder.build()); diff --git a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java index 8909f752f..b17ebb0ba 100644 --- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java +++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java @@ -16,6 +16,7 @@ package com.example.android.fingerprintdialog; +import android.app.Activity; import android.app.DialogFragment; import android.content.SharedPreferences; import android.hardware.fingerprint.FingerprintManager; @@ -54,6 +55,7 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment private FingerprintManager.CryptoObject mCryptoObject; private FingerprintUiHelper mFingerprintUiHelper; + private MainActivity mActivity; @Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder; @Inject InputMethodManager mInputMethodManager; @@ -135,6 +137,12 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment mFingerprintUiHelper.stopListening(); } + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mActivity = (MainActivity) activity; + } + /** * Sets the crypto object to be passed in when authenticating with fingerprint. */ @@ -167,7 +175,6 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment if (!checkPassword(mPassword.getText().toString())) { return; } - MainActivity activity = ((MainActivity) getActivity()); if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) { SharedPreferences.Editor editor = mSharedPreferences.edit(); editor.putBoolean(getString(R.string.use_fingerprint_to_authenticate_key), @@ -176,12 +183,12 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment if (mUseFingerprintFutureCheckBox.isChecked()) { // Re-create the key so that fingerprints including new ones are validated. - activity.createKey(); + mActivity.createKey(); mStage = Stage.FINGERPRINT; } } mPassword.setText(""); - ((MainActivity) getActivity()).onPurchased(false /* without Fingerprint */); + mActivity.onPurchased(false /* without Fingerprint */); dismiss(); } @@ -238,7 +245,7 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment public void onAuthenticated() { // Callback from FingerprintUiHelper. Let the activity know that authentication was // successful. - ((MainActivity) getActivity()).onPurchased(true /* withFingerprint */); + mActivity.onPurchased(true /* withFingerprint */); dismiss(); } diff --git a/samples/browseable/RuntimePermissions/_index.jd b/samples/browseable/RuntimePermissions/_index.jd index 75c6fee3e..a78f3b0cd 100644 --- a/samples/browseable/RuntimePermissions/_index.jd +++ b/samples/browseable/RuntimePermissions/_index.jd @@ -5,8 +5,9 @@ sample.group=System

- This sample shows runtime permissions available in the Android M and above. - Display the log to follow the execution. - If executed on an Android M device, an additional option to access contacts is shown. + This sample shows runtime permissions available in Android M and above. + Display the log on screen to follow the execution. + If executed on an Android M device, an additional option to access contacts is shown + that is declared with optional, M and above only permissions.

diff --git a/samples/browseable/RuntimePermissions/res/values/base-strings.xml b/samples/browseable/RuntimePermissions/res/values/base-strings.xml index 58d75f931..33c535ac6 100644 --- a/samples/browseable/RuntimePermissions/res/values/base-strings.xml +++ b/samples/browseable/RuntimePermissions/res/values/base-strings.xml @@ -21,9 +21,10 @@ diff --git a/samples/browseable/RuntimePermissions/res/values/strings.xml b/samples/browseable/RuntimePermissions/res/values/strings.xml index 82d7b719d..edd2c1532 100644 --- a/samples/browseable/RuntimePermissions/res/values/strings.xml +++ b/samples/browseable/RuntimePermissions/res/values/strings.xml @@ -1,5 +1,6 @@ + OK Total number of contacts: %1$,d\nFirst contact:%2$s No contacts stored on device. Contacts not loaded. @@ -17,5 +18,5 @@ Contacts Permissions have been granted. Contacts screen can now be opened. Permissions were not granted. Camera permission is needed to show the camera preview. - Contacts permissions are needed to demonstrate access to the contacts database. + Contacts permissions are needed to demonstrate access. diff --git a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java index 5f38bad8d..7abc538cf 100644 --- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java +++ b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/MainActivity.java @@ -16,7 +16,6 @@ package com.example.android.system.runtimepermissions; -import com.example.android.common.activities.SampleActivityBase; import com.example.android.common.logger.Log; import com.example.android.common.logger.LogFragment; import com.example.android.common.logger.LogWrapper; @@ -26,15 +25,20 @@ import com.example.android.system.runtimepermissions.contacts.ContactsFragment; import android.Manifest; import android.app.Activity; +import android.content.Context; import android.content.pm.PackageManager; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentTransaction; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.Toast; import android.widget.ViewAnimator; +import common.activities.SampleActivityBase; + /** * Launcher Activity that demonstrates the use of runtime permissions for Android M. * It contains a summary sample description, sample log and a Fragment that calls callbacks on this @@ -46,15 +50,18 @@ import android.widget.ViewAnimator; * android.Manifest.permission#WRITE_CONTACTS})) are requested when the 'Show and Add Contacts' * button is * clicked to display the first contact in the contacts database and to add a dummy contact - * directly - * to it. First, permissions are checked if they have already been granted through {@link - * android.app.Activity#checkSelfPermission(String)} (wrapped in {@link - * PermissionUtil#hasSelfPermission(Activity, String)} and {@link PermissionUtil#hasSelfPermission(Activity, - * String[])} for compatibility). If permissions have not been granted, they are requested through - * {@link Activity#requestPermissions(String[], int)} and the return value checked in {@link - * Activity#onRequestPermissionsResult(int, String[], int[])}. + * directly to it. Permissions are verified and requested through compat helpers in the support v4 + * library, in this Activity using {@link ActivityCompat}. + * First, permissions are checked if they have already been granted through {@link + * ActivityCompat#checkSelfPermission(Context, String)}. + * If permissions have not been granted, they are requested through + * {@link ActivityCompat#requestPermissions(Activity, String[], int)} and the return value checked + * in + * a callback to the {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback} + * interface. *

- * Before requesting permissions, {@link Activity#shouldShowRequestPermissionRationale(String)} + * Before requesting permissions, {@link ActivityCompat#shouldShowRequestPermissionRationale(Activity, + * String)} * should be called to provide the user with additional context for the use of permissions if they * have been denied previously. *

@@ -73,7 +80,8 @@ import android.widget.ViewAnimator; *

* (This class is based on the MainActivity used in the SimpleFragment sample template.) */ -public class MainActivity extends SampleActivityBase { +public class MainActivity extends SampleActivityBase + implements ActivityCompat.OnRequestPermissionsResultCallback { public static final String TAG = "MainActivity"; @@ -96,6 +104,10 @@ public class MainActivity extends SampleActivityBase { // Whether the Log Fragment is currently shown. private boolean mLogShown; + /** + * Root of the layout of this Activity. + */ + private View mLayout; /** * Called when the 'show camera' button is clicked. @@ -105,62 +117,122 @@ public class MainActivity extends SampleActivityBase { Log.i(TAG, "Show camera button pressed. Checking permission."); // BEGIN_INCLUDE(camera_permission) // Check if the Camera permission is already available. - if (PermissionUtil.hasSelfPermission(this, Manifest.permission.CAMERA)) { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + // Camera permission has not been granted. + + requestCameraPermission(); + + } else { + // Camera permissions is already available, show the camera preview. Log.i(TAG, "CAMERA permission has already been granted. Displaying camera preview."); showCameraPreview(); - } else { - // Camera permission has not been granted. - Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission."); - - // Provide an additional rationale to the user if the permission was not granted - // and the user would benefit from additional context for the use of the permission. - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { - Log.i(TAG, - "Displaying camera permission rationale to provide additional context."); - Toast.makeText(this, R.string.permission_camera_rationale, Toast.LENGTH_SHORT) - .show(); - } - - // Request Camera permission - requestPermissions(new String[]{Manifest.permission.CAMERA}, - REQUEST_CAMERA); } // END_INCLUDE(camera_permission) } + /** + * Requests the Camera permission. + * If the permission has been denied previously, a SnackBar will prompt the user to grant the + * permission, otherwise it is requested directly. + */ + private void requestCameraPermission() { + Log.i(TAG, "CAMERA permission has NOT been granted. Requesting permission."); + + // BEGIN_INCLUDE(camera_permission_request) + if (ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.CAMERA)) { + // Provide an additional rationale to the user if the permission was not granted + // and the user would benefit from additional context for the use of the permission. + // For example if the user has previously denied the permission. + Log.i(TAG, + "Displaying camera permission rationale to provide additional context."); + Snackbar.make(mLayout, R.string.permission_camera_rationale, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View view) { + ActivityCompat.requestPermissions(MainActivity.this, + new String[]{Manifest.permission.CAMERA}, + REQUEST_CAMERA); + } + }) + .show(); + } else { + + // Camera permission has not been granted yet. Request it directly. + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, + REQUEST_CAMERA); + } + // END_INCLUDE(camera_permission_request) + } + /** * Called when the 'show camera' button is clicked. * Callback is defined in resource layout definition. */ public void showContacts(View v) { Log.i(TAG, "Show contacts button pressed. Checking permissions."); + // Verify that all required contact permissions have been granted. - if (PermissionUtil.hasSelfPermission(this, PERMISSIONS_CONTACT)) { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) + != PackageManager.PERMISSION_GRANTED + || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) + != PackageManager.PERMISSION_GRANTED) { + // Contacts permissions have not been granted. + Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions."); + requestContactsPermissions(); + + } else { + + // Contact permissions have been granted. Show the contacts fragment. Log.i(TAG, "Contact permissions have already been granted. Displaying contact details."); - // Contact permissions have been granted. Show the contacts fragment. showContactDetails(); - } else { - // Contacts permissions have not been granted. - Log.i(TAG, "Contact permissions has NOT been granted. Requesting permission."); + } + } + + /** + * Requests the Contacts permissions. + * If the permission has been denied previously, a SnackBar will prompt the user to grant the + * permission, otherwise it is requested directly. + */ + private void requestContactsPermissions() { + // BEGIN_INCLUDE(contacts_permission_request) + if (ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.READ_CONTACTS) + || ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.WRITE_CONTACTS)) { // Provide an additional rationale to the user if the permission was not granted // and the user would benefit from additional context for the use of the permission. - if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { - Log.i(TAG, - "Displaying contacts permission rationale to provide additional context."); - Toast.makeText(this, R.string.permission_contacts_rationale, Toast.LENGTH_SHORT) - .show(); - } + // For example, if the request has been denied previously. + Log.i(TAG, + "Displaying contacts permission rationale to provide additional context."); - // contact permissions has not been granted (read and write contacts). Request them. - requestPermissions(PERMISSIONS_CONTACT, REQUEST_CONTACTS); + // Display a SnackBar with an explanation and a button to trigger the request. + Snackbar.make(mLayout, R.string.permission_contacts_rationale, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View view) { + ActivityCompat + .requestPermissions(MainActivity.this, PERMISSIONS_CONTACT, + REQUEST_CONTACTS); + } + }) + .show(); + } else { + // Contact permissions have not been granted yet. Request them directly. + ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS); } + // END_INCLUDE(contacts_permission_request) } + /** * Display the {@link CameraPreviewFragment} in the content area if the required Camera * permission has been granted. @@ -189,8 +261,8 @@ public class MainActivity extends SampleActivityBase { * Callback received when a permissions request has been completed. */ @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, - int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { if (requestCode == REQUEST_CAMERA) { // BEGIN_INCLUDE(permission_result) @@ -198,14 +270,15 @@ public class MainActivity extends SampleActivityBase { Log.i(TAG, "Received response for Camera permission request."); // Check if the only required permission has been granted - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Camera permission has been granted, preview can be displayed Log.i(TAG, "CAMERA permission has now been granted. Showing preview."); - Toast.makeText(this, R.string.permision_available_camera, Toast.LENGTH_SHORT) - .show(); + Snackbar.make(mLayout, R.string.permision_available_camera, + Snackbar.LENGTH_SHORT).show(); } else { Log.i(TAG, "CAMERA permission was NOT granted."); - Toast.makeText(this, R.string.permissions_not_granted, Toast.LENGTH_SHORT).show(); + Snackbar.make(mLayout, R.string.permissions_not_granted, + Snackbar.LENGTH_SHORT).show(); } // END_INCLUDE(permission_result) @@ -217,11 +290,14 @@ public class MainActivity extends SampleActivityBase { // checked. if (PermissionUtil.verifyPermissions(grantResults)) { // All required permissions have been granted, display contacts fragment. - Toast.makeText(this, R.string.permision_available_contacts, Toast.LENGTH_SHORT) + Snackbar.make(mLayout, R.string.permision_available_contacts, + Snackbar.LENGTH_SHORT) .show(); } else { Log.i(TAG, "Contacts permissions were NOT granted."); - Toast.makeText(this, R.string.permissions_not_granted, Toast.LENGTH_SHORT).show(); + Snackbar.make(mLayout, R.string.permissions_not_granted, + Snackbar.LENGTH_SHORT) + .show(); } } else { @@ -291,6 +367,7 @@ public class MainActivity extends SampleActivityBase { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + mLayout = findViewById(R.id.sample_main_layout); if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); diff --git a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java index d0742ead9..b9be6258e 100644 --- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java +++ b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/PermissionUtil.java @@ -18,7 +18,6 @@ package com.example.android.system.runtimepermissions; import android.app.Activity; import android.content.pm.PackageManager; -import android.os.Build; /** * Utility class that wraps access to the runtime permissions API in M and provides basic helper @@ -33,6 +32,11 @@ public abstract class PermissionUtil { * @see Activity#onRequestPermissionsResult(int, String[], int[]) */ public static boolean verifyPermissions(int[] grantResults) { + // At least one result must be checked. + if(grantResults.length < 1){ + return false; + } + // Verify that each required permission has been granted, otherwise return false. for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { @@ -42,43 +46,4 @@ public abstract class PermissionUtil { return true; } - /** - * Returns true if the Activity has access to all given permissions. - * Always returns true on platforms below M. - * - * @see Activity#checkSelfPermission(String) - */ - public static boolean hasSelfPermission(Activity activity, String[] permissions) { - // Below Android M all permissions are granted at install time and are already available. - if (!isMNC()) { - return true; - } - - // Verify that all required permissions have been granted - for (String permission : permissions) { - if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { - return false; - } - } - return true; - } - - /** - * Returns true if the Activity has access to a given permission. - * Always returns true on platforms below M. - * - * @see Activity#checkSelfPermission(String) - */ - public static boolean hasSelfPermission(Activity activity, String permission) { - // Below Android M all permissions are granted at install time and are already available. - if (!isMNC()) { - return true; - } - - return activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; - } - - public static boolean isMNC() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; - } } diff --git a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java index b35bfebc0..d38195f57 100644 --- a/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java +++ b/samples/browseable/RuntimePermissions/src/com.example.android.system.runtimepermissions/RuntimePermissionsFragment.java @@ -16,6 +16,7 @@ package com.example.android.system.runtimepermissions; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; @@ -32,16 +33,16 @@ public class RuntimePermissionsFragment extends Fragment { View root = inflater.inflate(R.layout.fragment_main, null); // BEGIN_INCLUDE(m_only_permission) - if (!PermissionUtil.isMNC()) { + if (Build.VERSION.SDK_INT < 23) { /* - The contacts permissions have been declared in the AndroidManifest for Android M only. - They are not available on older platforms, so we are hiding the button to access the - contacts database. + The contacts permissions have been declared in the AndroidManifest for Android M and + above only. They are not available on older platforms, so we are hiding the button to + access the contacts database. This shows how new runtime-only permissions can be added, that do not apply to older platform versions. This can be useful for automated updates where additional permissions might prompt the user on upgrade. */ - root.findViewById(R.id.button_camera).setVisibility(View.GONE); + root.findViewById(R.id.button_contacts).setVisibility(View.GONE); } // END_INCLUDE(m_only_permission) diff --git a/samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java b/samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java new file mode 100644 index 000000000..ac3928ef0 --- /dev/null +++ b/samples/browseable/RuntimePermissions/src/common.activities/SampleActivityBase.java @@ -0,0 +1,53 @@ +/* +* Copyright 2013 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package common.activities; + +import com.example.android.common.logger.Log; +import com.example.android.common.logger.LogWrapper; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + + +/** + * Base launcher activity, to handle most of the common plumbing for samples. + */ +public class SampleActivityBase extends AppCompatActivity { + + public static final String TAG = "SampleActivityBase"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + initializeLogging(); + } + + /** Set up targets to receive log data */ + public void initializeLogging() { + // Using Log, front-end to the logging chain, emulates android.util.log method signatures. + // Wraps Android's native log framework + LogWrapper logWrapper = new LogWrapper(); + Log.setLogNode(logWrapper); + + Log.i(TAG, "Ready"); + } +} diff --git a/samples/browseable/RuntimePermissionsBasic/_index.jd b/samples/browseable/RuntimePermissionsBasic/_index.jd index c4b5d4ca8..4fe611862 100644 --- a/samples/browseable/RuntimePermissionsBasic/_index.jd +++ b/samples/browseable/RuntimePermissionsBasic/_index.jd @@ -5,7 +5,6 @@ sample.group=System

- This sample shows runtime permissions available in the Android M and above. This sample shows a basic implementation for requesting permissions at runtime. Click the button to request the Camera permission and open a full-screen camera preview. Note: The "RuntimePermissions" sample provides a more complete overview over the runtime permission features available. diff --git a/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml b/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml index c3bf99c5f..146b8b1e7 100644 --- a/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml +++ b/samples/browseable/RuntimePermissionsBasic/res/layout/activity_main.xml @@ -14,27 +14,28 @@ limitations under the License. --> - + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/horizontal_page_margin" + android:text="@string/intro" />