From 37c91fcaed2f3c76ccc28fbad837dbfb747eb817 Mon Sep 17 00:00:00 2001 From: Andrew Stephan Date: Thu, 27 Feb 2014 12:00:53 -0500 Subject: [PATCH 1/5] runs in android now but doesn't work yet --- .gitignore | 2 +- plugin.xml | 47 +++ src/Android/Library/res/anim/image_pop_in.xml | 11 + .../Library/res/drawable-hdpi/icon.png | Bin 0 -> 4147 bytes .../Library/res/drawable-hdpi/image_bg.9.png | Bin 0 -> 1574 bytes .../res/drawable-hdpi/loading_icon.png | Bin 0 -> 3071 bytes .../Library/res/drawable-ldpi/icon.png | Bin 0 -> 1723 bytes .../drawable-mdpi/ic_action_discard_dark.png | Bin 0 -> 415 bytes .../drawable-mdpi/ic_action_discard_light.png | Bin 0 -> 386 bytes .../res/drawable-mdpi/ic_action_done_dark.png | Bin 0 -> 454 bytes .../drawable-mdpi/ic_action_done_light.png | Bin 0 -> 400 bytes .../Library/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2645 bytes .../Library/res/drawable-mdpi/icon.png | Bin 0 -> 2574 bytes .../drawable-xhdpi/ic_action_discard_dark.png | Bin 0 -> 724 bytes .../ic_action_discard_light.png | Bin 0 -> 684 bytes .../drawable-xhdpi/ic_action_done_dark.png | Bin 0 -> 825 bytes .../drawable-xhdpi/ic_action_done_light.png | Bin 0 -> 759 bytes .../res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 5213 bytes .../Library/res/drawable/grid_background.xml | 12 + .../actionbar_custom_view_done_discard.xml | 27 ++ .../res/layout/actionbar_discard_button.xml | 33 ++ .../res/layout/actionbar_done_button.xml | 34 ++ .../Library/res/layout/multiselectorgrid.xml | 33 ++ .../multiimagechooser_strings_de.xml | 7 + .../multiimagechooser_strings_es.xml | 7 + .../multiimagechooser_strings_fr.xml | 6 + .../multiimagechooser_strings_hu.xml | 8 + .../multiimagechooser_strings_ja.xml | 7 + .../multiimagechooser_strings_ko.xml | 7 + .../values/multiimagechooser_strings_en.xml | 9 + src/Android/Library/res/values/themes.xml | 5 + src/Android/Library/src/ImageFetcher.java | 368 ++++++++++++++++ .../src/MultiImageChooserActivity.java | 395 ++++++++++++++++++ .../synconset/ImagePicker/ImagePicker.java | 44 ++ 34 files changed, 1061 insertions(+), 1 deletion(-) create mode 100644 src/Android/Library/res/anim/image_pop_in.xml create mode 100644 src/Android/Library/res/drawable-hdpi/icon.png create mode 100644 src/Android/Library/res/drawable-hdpi/image_bg.9.png create mode 100644 src/Android/Library/res/drawable-hdpi/loading_icon.png create mode 100644 src/Android/Library/res/drawable-ldpi/icon.png create mode 100644 src/Android/Library/res/drawable-mdpi/ic_action_discard_dark.png create mode 100644 src/Android/Library/res/drawable-mdpi/ic_action_discard_light.png create mode 100644 src/Android/Library/res/drawable-mdpi/ic_action_done_dark.png create mode 100644 src/Android/Library/res/drawable-mdpi/ic_action_done_light.png create mode 100644 src/Android/Library/res/drawable-mdpi/ic_launcher.png create mode 100644 src/Android/Library/res/drawable-mdpi/icon.png create mode 100644 src/Android/Library/res/drawable-xhdpi/ic_action_discard_dark.png create mode 100644 src/Android/Library/res/drawable-xhdpi/ic_action_discard_light.png create mode 100644 src/Android/Library/res/drawable-xhdpi/ic_action_done_dark.png create mode 100644 src/Android/Library/res/drawable-xhdpi/ic_action_done_light.png create mode 100644 src/Android/Library/res/drawable-xhdpi/ic_launcher.png create mode 100644 src/Android/Library/res/drawable/grid_background.xml create mode 100644 src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml create mode 100644 src/Android/Library/res/layout/actionbar_discard_button.xml create mode 100644 src/Android/Library/res/layout/actionbar_done_button.xml create mode 100644 src/Android/Library/res/layout/multiselectorgrid.xml create mode 100644 src/Android/Library/res/values-de/multiimagechooser_strings_de.xml create mode 100644 src/Android/Library/res/values-es/multiimagechooser_strings_es.xml create mode 100644 src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml create mode 100644 src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml create mode 100644 src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml create mode 100644 src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml create mode 100644 src/Android/Library/res/values/multiimagechooser_strings_en.xml create mode 100644 src/Android/Library/res/values/themes.xml create mode 100644 src/Android/Library/src/ImageFetcher.java create mode 100644 src/Android/Library/src/MultiImageChooserActivity.java create mode 100644 src/Android/com/synconset/ImagePicker/ImagePicker.java diff --git a/.gitignore b/.gitignore index 80e7971..2e9df62 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ # Xcode 4 xcuserdata/ project.xcworkspace/ - +tags diff --git a/plugin.xml b/plugin.xml index 475b5d2..e19ae37 100644 --- a/plugin.xml +++ b/plugin.xml @@ -56,5 +56,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Android/Library/res/anim/image_pop_in.xml b/src/Android/Library/res/anim/image_pop_in.xml new file mode 100644 index 0000000..3b1f7e9 --- /dev/null +++ b/src/Android/Library/res/anim/image_pop_in.xml @@ -0,0 +1,11 @@ + + diff --git a/src/Android/Library/res/drawable-hdpi/icon.png b/src/Android/Library/res/drawable-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 GIT binary patch literal 4147 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 literal 0 HcmV?d00001 diff --git a/src/Android/Library/res/drawable-hdpi/image_bg.9.png b/src/Android/Library/res/drawable-hdpi/image_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..9835be619c33d45984f74d151ccaf57217ba87bb GIT binary patch literal 1574 zcmV+>2HE+EP)R<9V@yPAGIebNz+PZO*d}8L zyAVNWeJLnT!Ao!bAG{I-C7>7LJBkpH?fJ>~grCP~(&8>mo&z7m3q3xcJip&LM>yXn zE-td;-x%h#jyF_X02<;Vc#}u~iG&thQjrt|sB6-q=0wRsyyQj$Um=CcBIBiBpp}HB zfXhTGktXq06d>1=iaM2=lO)LrXf#Fc5uu?(CaNT+;)FV&Co%{FFcO*P93wEGwhYuM z>b)XKmgOp9j|f%E)Jh`LB!LE;IE%<8atIS)Cf=6FJ%`ADzZrGqpw2ARZGZ7jA7$vcn;VXD(R5InR zG)N-zZ~}3{Vxoj7C2kN-!bOxt0WQ=ea1eIXTZsDe;3EsorbF`ukBibWPnKkgPvI(- zgZQ+HZ~}26H{m79iAti1sE#74P?xCB1JO6oL{^B+MY9>;D|p1?0+a;PicGP8){8a(8sF7^b2&*v*xSXk%@1OmgGo0~)H>+8eeSY2Hmi58Ta zc6N4#XJ=>s%*n}VCNCclbr5?KtF=(-)FVQZu+%e16sfiY^Vm*eB(zmT5~(Y4j+RtGvYR~D*8cj~A!^Y-`mhn!AlOp8=QTip~j*@#}R z?<7x6=+-;9me`1Evd~M|JM~LTOFjMl{bMZgID>`INX& zk*jXvGs1`c^fvLX^53bT(9X_I%huM`3zm1X(2b3a5pwk{@fEtX0eh)S7AhW{^zH5K z1uQfop+6GOh);-*a4U6F7Ah`C8Lh3Yg)H=Rp=)bvqvT55K7B)cPBi1<@dNQS(TaQ03bjxp3yoOlAJ|8q z679r&JdOs1ihEFFQ&Uqh3ynxQadZd1g$P0x_q^a%Usk=&*@ zWQHCliya&sjM{8A78ruMy1L2bThh<*%N!kPErj zAh;lkCoNboX(3-VX(3aZw2-QvwD3B!K(f*9u{sMxciJqFaMNm%C9%r1nsn8)8e^19 ztI3q6)fiOMYILYyJ*`HWvvh2p(gkVxtHo+na#KRgQi_FnO3A8uN(rInDe(xkGa8}x YA9J6cX#E3w;s5{u07*qoM6N<$g7C-pZU6uP literal 0 HcmV?d00001 diff --git a/src/Android/Library/res/drawable-hdpi/loading_icon.png b/src/Android/Library/res/drawable-hdpi/loading_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3490f83dedccc68d858b2a8ad80760890d5e726a GIT binary patch literal 3071 zcmVk_frZ}1546Ds106+UF`aU( zM6(&3{9?qx{214g{7OMnQxfd1C|0IqrHz__GMSDxx@8FlnhZHo154lsj9*|NU-G{F zv3v1ydEfKiJ!j8(FI;{zXBg(}zR$DgxqHs;v(G+Ts#Ge}LrC2NI1(5E4EO&I1$x!3 z+&$O`ECLp^yB1obHrK0IN-}_ux*1TwmB0Z}RP8>j1X_V<)~HWv6{=|fA@vc!uYhCn z(5@RXA2{C{^?J3N+ZjMe-Guv#{}IqTclEjl8-Yo{RBKeZ{f%u5;2-|4fwK!$ulw*2 za9(>OG<7KOJ$60(JUJWpjJLA22MDQ$0RJTHwevnu^8?dOXa@QRl-~dhvqmijZvg-5 zc@a3SPUXeG?{FWM`PQgaDHPu65K`}l8=8**&H}zrr^-vf3D&4`n*kJV$W)6mFb$Yw zjoJ`H>75lJbuZv1U?Q$})Zz+jlrFW5YHb3R0fTB3ECXM&Mm-l*)14e4^~u09Z2z5Y zV+GLC1RO!_hYxNdvFqUn;-A?a2}K40M>OGF3$<7b+z?Z-ZpI{>i=q}Ins6VETKwJ` zwK1+v-3M#bCfwM+7Q^cqV18Vcx+Sk9+#AsY=hdvmg2>7QBc$F3I2IU;a|jOf*8#wO zzSz6KJN|mx|GN~JYmNG_Xq`KdWPqWimQk&>+@Wi&QG4d0Q9XpzgMrh4F~A8xkCZ>P z1^65A1n`74YDpXo!w^!h0rskPFR5W$@=>QcLh6Hn@xU1*_sS5Ofl>aNCZt{j%mQXw zqiiG@ouQ`BKkZ*uG=$UxfXTpx;0HL@_Z|g{ zZ^jw?J3{JKV2U;Bqk?q+8ejk+bqSb&TW{FEX!#xJ16&785K`X=%&2D%V`?5SqzHK# z8GbQu?O^9t}sf4zcpKjblaobKC_3%MA;f5aXm&M!@}xVgaLSrh|a2W|uA zSfkdoztJ+f^|(@{q=0V%?^jBd)(yQr`T5FcCa&wCOJCsYzz=bum05TLIJToMok2*w z8mKzFjoJhJB=@1-=0Uze+qe`?f|QW)GMM3`kLLIdHr+>JM2JC^eOPcBY&)>S^2} z&wo;g8GvhR6rvbFNc~yTDYXnZ#v1in3_U`yM!f-y#I2l#GSRm=GqHNWjX>YPI_rQj zjf}3dM!knybz2))wJ)yCnaOMbA@v~Ks3n97E@pmVR{2@6MlAx)C1KU71{eCaXCg)q z7)SVoYl=1M@d&b`V2zp$+#Xo12d?dzh+%-!1IxdSTa4*y+=6pwgz&AbLNgh_cfl_O zR-0URsM!FlQ6J-iYC{+%q;AO~H){_#EwKC>z|4#?^W=V9IA#cAvIs2N0MlyCaJvG2 zH)H6x;EXIXQyaiLx<&_7*@6p@>gqg6n5*IhA$97-rYt>R7|#9MSzfe8y_-Q^Ay}h6 z0R9?KrJ3Xi^F*>3pe3;UtPHY>W_DotgVGAhVt~&DmVYLLtfKi-VENQp1hTAHg&H7> zFTQh>87E}x=)^Lm(g#huZFq3WvsWj1L;o8nd0=4qtrTT|HGZ6(Go|zE7O6maQ|}wa z=(}#WFI@hUYDKJ38->�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 literal 0 HcmV?d00001 diff --git a/src/Android/Library/res/drawable-mdpi/ic_action_done_dark.png b/src/Android/Library/res/drawable-mdpi/ic_action_done_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b8fb4f84207d0c1abb3fb57a2c91d25302c9d422 GIT binary patch literal 454 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(eqE9(%ethIkx*J9Q%;qoaVs^OhF2UYF{rPAda7LZ#*}2>!_&_`>DNqNmCp zC95tkxNwqnqQ{@3!5tB&=f-k%-dI~U>ATU)nP)9dq^o~Y{4ZYPJz>(SPlvv#y(nH% zS9?26VEVj}Bh#-|OS-(=bbN6^_sxg0-zr3|uF$pHfBY;fDVH>j_Z?;g~1gO{SUvTcJ!FK_V8KnElhO1BAQ#KqtPlF!5<^)*`gLbZOUQmFA}>n z*tUF#e|-M=fz+P!$5{BKRHkcY3(FKTeRJ~LWMusF{<->%f0`cfbZnAvzHshyAJDt1 zC9V-ADTyViR>?)FK#IZ0z|dINz*yJND8$g%%Gkon$Wq(D(8|C-b%96&iiX_$l+3hB zxCR4@5F?-lODjV|hz3sWTSh<)k{}y`^V3So6N^$A98>a>QWZRN6Vp?JQWH}u3s0un Q02MKKy85}Sb4q9e08uumk4UOiAAEE)4(M`_JqL@;D1TB8!2v z2N=7Z%(eqE)_S@)hIkx*dvPJxAqSb`AM-yrbVt=NOg^a5l$fybH^(;B`wl$Y4zG*2 z>+G{(X4=Ih>BF*350WKZrg-1`-}R{O@8`X{dAt6;%;Kr7XjKh&($wF4V8deV$EwdH zgsg(r8LE7#`SEY|&Lo$^&%YR&E4})3di%|S#9Y-m_wK1|6XNoH_v!JQBRzAN4<48Q ztsBUyHpkMa;NkisU79mn?1d#)PO%q?_ABDMu;~5a?!PzQ#e|qy3Ync?1Ug)`#5JNM zC9x#cD!C{XNHG{07#iyu0FgzAk+GGrrImq+wt=CQfx)vUzS~hW3VtQ&&YGO)d;mK4RpdtoOS3j3^ HP6{XPn literal 0 HcmV?d00001 diff --git a/src/Android/Library/res/drawable-mdpi/ic_launcher.png b/src/Android/Library/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9dc7f2185cc16b43440c84c6364209f03a4a6091 GIT binary patch literal 2645 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 literal 0 HcmV?d00001 diff --git a/src/Android/Library/res/drawable-xhdpi/ic_action_discard_light.png b/src/Android/Library/res/drawable-xhdpi/ic_action_discard_light.png new file mode 100644 index 0000000000000000000000000000000000000000..24256f15c99031d16bc6a0e6d8100e6fa66af508 GIT binary patch literal 684 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 + + + \ No newline at end of file diff --git a/src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml b/src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml new file mode 100644 index 0000000..0b61fb9 --- /dev/null +++ b/src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/src/Android/Library/res/layout/actionbar_discard_button.xml b/src/Android/Library/res/layout/actionbar_discard_button.xml new file mode 100644 index 0000000..3e72461 --- /dev/null +++ b/src/Android/Library/res/layout/actionbar_discard_button.xml @@ -0,0 +1,33 @@ + + + + diff --git a/src/Android/Library/res/layout/actionbar_done_button.xml b/src/Android/Library/res/layout/actionbar_done_button.xml new file mode 100644 index 0000000..a6071c6 --- /dev/null +++ b/src/Android/Library/res/layout/actionbar_done_button.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/src/Android/Library/res/layout/multiselectorgrid.xml b/src/Android/Library/res/layout/multiselectorgrid.xml new file mode 100644 index 0000000..6e22b5f --- /dev/null +++ b/src/Android/Library/res/layout/multiselectorgrid.xml @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/src/Android/Library/res/values-de/multiimagechooser_strings_de.xml b/src/Android/Library/res/values-de/multiimagechooser_strings_de.xml new file mode 100644 index 0000000..32123f1 --- /dev/null +++ b/src/Android/Library/res/values-de/multiimagechooser_strings_de.xml @@ -0,0 +1,7 @@ + + + MultiImageChooser + Free version - Images left: %d + There was an error opening the images database. Please report the problem. + Requesting thumbnails, please be patient + diff --git a/src/Android/Library/res/values-es/multiimagechooser_strings_es.xml b/src/Android/Library/res/values-es/multiimagechooser_strings_es.xml new file mode 100644 index 0000000..fe05862 --- /dev/null +++ b/src/Android/Library/res/values-es/multiimagechooser_strings_es.xml @@ -0,0 +1,7 @@ + + + MultiImageChooser + Solicitando miniaturas. Por favor, espere… + Versión gratuita - Imágenes restantes: %d + Error al abrir la base de datos de imágenes. + \ No newline at end of file diff --git a/src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml b/src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml new file mode 100644 index 0000000..964f190 --- /dev/null +++ b/src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml @@ -0,0 +1,6 @@ + + "MultiImageChooser" + "La version gratuite - gauche Images:%d" + "Il y avait une erreur d'ouvrir la base de données images. S'il vous plaît signaler le problème." + "Demande de vignettes, s'il vous plaît soyez patient" + \ No newline at end of file diff --git a/src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml b/src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml new file mode 100644 index 0000000..5bb897c --- /dev/null +++ b/src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml @@ -0,0 +1,8 @@ + + + MultiImageChooser + + Ingyenes verzió - hátralévő képek: %d + Képadatbázis megnyitási hiba történt. Kérjük, jelentse a problémát. + Miniatűrök lekérése, kérjük legyen türelemmel + \ No newline at end of file diff --git a/src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml b/src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml new file mode 100644 index 0000000..92f084a --- /dev/null +++ b/src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml @@ -0,0 +1,7 @@ + + + MultiImageChooser + 無料版 - 残りの画像: %d + 画像データベースを開く際にエラーがありました。問題を報告してください。 + サムネイルをリクエスト中です。お待ちください。 + diff --git a/src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml b/src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml new file mode 100644 index 0000000..4454379 --- /dev/null +++ b/src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml @@ -0,0 +1,7 @@ + + + MultiImageChooser + 무료 버전 - 남은 이미지: %d + 이미지 데이터베이스를 여는 데 오류가 발생했습니다. 문제를 보고하세요. + 썸네일 요청 중. 기다려주세요 + diff --git a/src/Android/Library/res/values/multiimagechooser_strings_en.xml b/src/Android/Library/res/values/multiimagechooser_strings_en.xml new file mode 100644 index 0000000..2566b2f --- /dev/null +++ b/src/Android/Library/res/values/multiimagechooser_strings_en.xml @@ -0,0 +1,9 @@ + + + MultiImageChooser + Free version - Images left: %d + There was an error opening the images database. Please report the problem. + Requesting thumbnails, please be patient + Cancel + OK + diff --git a/src/Android/Library/res/values/themes.xml b/src/Android/Library/res/values/themes.xml new file mode 100644 index 0000000..2ec73cf --- /dev/null +++ b/src/Android/Library/res/values/themes.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/Android/Library/src/ImageFetcher.java b/src/Android/Library/src/ImageFetcher.java new file mode 100644 index 0000000..f384862 --- /dev/null +++ b/src/Android/Library/src/ImageFetcher.java @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2010 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 com.synconset; + +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.provider.MediaStore; +import android.util.Log; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ImageView; + +/** + * This helper class download images from the Internet and binds those with the + * provided ImageView. + * + *

+ * It requires the INTERNET permission, which should be added to your + * application's manifest file. + *

+ * + * A local cache of downloaded images is maintained internally to improve + * performance. + */ +public class ImageFetcher { + + private int colWidth; + private long origId; + private ExecutorService executor; + + public ImageFetcher() { + executor = Executors.newCachedThreadPool(); + } + + public void fetch(Integer id, ImageView imageView, int colWidth) { + resetPurgeTimer(); + this.colWidth = colWidth; + this.origId = id; + Bitmap bitmap = getBitmapFromCache(id); + + if (bitmap == null) { + forceDownload(id, imageView); + } else { + cancelPotentialDownload(id, imageView); + imageView.setImageBitmap(bitmap); + } + } + + /** + * Same as download but the image is always downloaded and the cache is not + * used. Kept private at the moment as its interest is not clear. + */ + private void forceDownload(Integer position, ImageView imageView) { + if (position == null) { + imageView.setImageDrawable(null); + return; + } + + if (cancelPotentialDownload(position, imageView)) { + BitmapFetcherTask task = new BitmapFetcherTask(imageView.getContext(), imageView); + DownloadedDrawable downloadedDrawable = new DownloadedDrawable(imageView.getContext(), task, origId); + imageView.setImageDrawable(downloadedDrawable); + imageView.setMinimumHeight(colWidth); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + task.executeOnExecutor(executor, position); + } else { + try { + task.execute(position); + } catch (RejectedExecutionException e) { + // Oh :( + } + } + + } + } + + /** + * Returns true if the current download has been canceled or if there was no + * download in progress on this image view. Returns false if the download in + * progress deals with the same url. The download is not stopped in that + * case. + */ + private static boolean cancelPotentialDownload(Integer position, ImageView imageView) { + BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); + long origId = getOrigId(imageView); + + if (bitmapDownloaderTask != null) { + Integer bitmapPosition = bitmapDownloaderTask.position; + if ((bitmapPosition == null) || (!bitmapPosition.equals(position))) { + Log.d("DAVID", "Canceling..."); + MediaStore.Images.Thumbnails.cancelThumbnailRequest(imageView.getContext().getContentResolver(), + origId, 12345); + bitmapDownloaderTask.cancel(true); + } else { + return false; + } + } + return true; + } + + /** + * @param imageView + * Any imageView + * @return Retrieve the currently active download task (if any) associated + * with this imageView. null if there is no such task. + */ + private static BitmapFetcherTask getBitmapDownloaderTask(ImageView imageView) { + if (imageView != null) { + Drawable drawable = imageView.getDrawable(); + if (drawable instanceof DownloadedDrawable) { + DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable; + return downloadedDrawable.getBitmapDownloaderTask(); + } + } + return null; + } + + private static long getOrigId(ImageView imageView) { + if (imageView != null) { + Drawable drawable = imageView.getDrawable(); + if (drawable instanceof DownloadedDrawable) { + DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable; + return downloadedDrawable.getOrigId(); + } + } + return -1; + } + + /** + * The actual AsyncTask that will asynchronously download the image. + */ + class BitmapFetcherTask extends AsyncTask { + private Integer position; + private final WeakReference imageViewReference; + private final Context mContext; + + public BitmapFetcherTask(Context context, ImageView imageView) { + imageViewReference = new WeakReference(imageView); + mContext = context; + } + + /** + * Actual download method. + */ + @Override + protected Bitmap doInBackground(Integer... params) { + position = params[0]; + if (isCancelled()) { + return null; + } + Bitmap thumb = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), position, 12345, + MediaStore.Images.Thumbnails.MICRO_KIND, null); + if (isCancelled()) { + return null; + } + if (thumb == null) { + return null; + } else { + if (isCancelled()) { + return null; + } else { + return thumb; + } + } + + } + + private void setInvisible() { + Log.d("COLLAGE", "Setting something invisible..."); + if (imageViewReference != null) { + final ImageView imageView = imageViewReference.get(); + BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); + if (this == bitmapDownloaderTask) { + imageView.setVisibility(View.GONE); + imageView.setClickable(false); + imageView.setEnabled(false); + } + } + } + + /** + * Once the image is downloaded, associates it to the imageView + */ + @Override + protected void onPostExecute(Bitmap bitmap) { + if (isCancelled()) { + bitmap = null; + } + addBitmapToCache(position, bitmap); + if (imageViewReference != null) { + ImageView imageView = imageViewReference.get(); + BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); + if (this == bitmapDownloaderTask) { + imageView.setImageBitmap(bitmap); + Animation anim = AnimationUtils.loadAnimation(imageView.getContext(), android.R.anim.fade_in); + imageView.setAnimation(anim); + anim.start(); + } + } else { + setInvisible(); + } + } + } + + /** + * A fake Drawable that will be attached to the imageView while the download + * is in progress. + * + *

+ * Contains a reference to the actual download task, so that a download task + * can be stopped if a new binding is required, and makes sure that only the + * last started download process can bind its result, independently of the + * download finish order. + *

+ */ + static class DownloadedDrawable extends ColorDrawable { + private final WeakReference bitmapDownloaderTaskReference; + private long origId; + + public DownloadedDrawable(Context mContext, BitmapFetcherTask bitmapDownloaderTask, long origId) { + super(Color.TRANSPARENT); + bitmapDownloaderTaskReference = new WeakReference(bitmapDownloaderTask); + this.origId = origId; + } + + public long getOrigId() { + return origId; + } + + public BitmapFetcherTask getBitmapDownloaderTask() { + return bitmapDownloaderTaskReference.get(); + } + } + + /* + * Cache-related fields and methods. + * + * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the + * Garbage Collector. + */ + + private static final int HARD_CACHE_CAPACITY = 100; + private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds + + // Hard cache, with a fixed maximum capacity and a life duration + private final HashMap sHardBitmapCache = new LinkedHashMap( + HARD_CACHE_CAPACITY / 2, 0.75f, true) { + @Override + protected boolean removeEldestEntry(LinkedHashMap.Entry eldest) { + if (size() > HARD_CACHE_CAPACITY) { + // Entries push-out of hard reference cache are transferred to + // soft reference cache + sSoftBitmapCache.put(eldest.getKey(), new SoftReference(eldest.getValue())); + return true; + } else + return false; + } + }; + + // Soft cache for bitmaps kicked out of hard cache + private final static ConcurrentHashMap> sSoftBitmapCache = new ConcurrentHashMap>( + HARD_CACHE_CAPACITY / 2); + + private final Handler purgeHandler = new Handler(); + + private final Runnable purger = new Runnable() { + public void run() { + clearCache(); + } + }; + + /** + * Adds this bitmap to the cache. + * + * @param bitmap + * The newly downloaded bitmap. + */ + private void addBitmapToCache(Integer position, Bitmap bitmap) { + if (bitmap != null) { + synchronized (sHardBitmapCache) { + sHardBitmapCache.put(position, bitmap); + } + } + } + + /** + * @param position + * The URL of the image that will be retrieved from the cache. + * @return The cached bitmap or null if it was not found. + */ + private Bitmap getBitmapFromCache(Integer position) { + // First try the hard reference cache + synchronized (sHardBitmapCache) { + final Bitmap bitmap = sHardBitmapCache.get(position); + if (bitmap != null) { + Log.d("CACHE ****** ", "Hard hit!"); + // Bitmap found in hard cache + // Move element to first position, so that it is removed last + return bitmap; + } + } + + // Then try the soft reference cache + SoftReference bitmapReference = sSoftBitmapCache.get(position); + if (bitmapReference != null) { + final Bitmap bitmap = bitmapReference.get(); + if (bitmap != null) { + // Bitmap found in soft cache + Log.d("CACHE ****** ", "Soft hit!"); + return bitmap; + } else { + // Soft reference has been Garbage Collected + sSoftBitmapCache.remove(position); + } + } + + return null; + } + + /** + * Clears the image cache used internally to improve performance. Note that + * for memory efficiency reasons, the cache will automatically be cleared + * after a certain inactivity delay. + */ + public void clearCache() { + // sHardBitmapCache.clear(); + // sSoftBitmapCache.clear(); + } + + /** + * Allow a new delay before the automatic cache clear is done. + */ + private void resetPurgeTimer() { + // purgeHandler.removeCallbacks(purger); + // purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE); + } +} diff --git a/src/Android/Library/src/MultiImageChooserActivity.java b/src/Android/Library/src/MultiImageChooserActivity.java new file mode 100644 index 0000000..a7d0b3b --- /dev/null +++ b/src/Android/Library/src/MultiImageChooserActivity.java @@ -0,0 +1,395 @@ +package com.synconset; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import com.wymsee.apps.synconset.R; +import android.app.Activity; +import android.app.ActionBar; +import android.app.LoaderManager; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Intent; +import android.content.Loader; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.os.Bundle; +import android.provider.MediaStore; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.BaseAdapter; +import android.widget.GridView; +import android.widget.ImageView; +import android.widget.TextView; + +public class MultiImageChooserActivity extends Activity implements OnItemClickListener, + LoaderManager.LoaderCallbacks { + private static final String TAG = "Collage"; + public static final String COL_WIDTH_KEY = "COL_WIDTH"; + public static final String FLURRY_EVENT_ADD_MULTIPLE_IMAGES = "Add multiple images"; + + // El tamaño por defecto es 100 porque los thumbnails MICRO_KIND son de + // 96x96 + private static final int DEFAULT_COLUMN_WIDTH = 120; + + public static final int NOLIMIT = -1; + public static final String MAX_IMAGES_KEY = "MAX_IMAGES"; + + private ImageAdapter ia; + + private Cursor imagecursor, actualimagecursor; + private int image_column_index, actual_image_column_index; + private int colWidth; + + private static final int CURSORLOADER_THUMBS = 0; + private static final int CURSORLOADER_REAL = 1; + + private Set fileNames = new HashSet(); + + private SparseBooleanArray checkStatus = new SparseBooleanArray(); + + private TextView freeLabel = null; + private int maxImages; + private boolean unlimitedImages = false; + + private GridView gridView; + + private final ImageFetcher fetcher = new ImageFetcher(); + + private int selectedColor = Color.GREEN; + private boolean shouldRequestThumb = true; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.multiselectorgrid); + fileNames.clear(); + + maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT); + + unlimitedImages = maxImages == NOLIMIT; + if (!unlimitedImages) { + freeLabel = (TextView) findViewById(R.id.label_images_left); + freeLabel.setVisibility(View.VISIBLE); + updateLabel(); + } + + colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH); + + Display display = getWindowManager().getDefaultDisplay(); + @SuppressWarnings("deprecation") + int width = display.getWidth(); + int testColWidth = width / 3; + + if (testColWidth > colWidth) { + colWidth = width / 4; + } + + // int bgColor = getIntent().getIntExtra("BG_COLOR", Color.BLACK); + + gridView = (GridView) findViewById(R.id.gridview); + gridView.setColumnWidth(colWidth); + gridView.setOnItemClickListener(this); + gridView.setOnScrollListener(new OnScrollListener() { + + private int lastFirstItem = 0; + private long timestamp = System.currentTimeMillis(); + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == SCROLL_STATE_IDLE) { + Log.d(TAG, "IDLE - Reload!"); + shouldRequestThumb = true; + ia.notifyDataSetChanged(); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + float dt = System.currentTimeMillis() - timestamp; + if (firstVisibleItem != lastFirstItem) { + double speed = 1 / dt * 1000; + lastFirstItem = firstVisibleItem; + timestamp = System.currentTimeMillis(); + Log.d(TAG, "Speed: " + speed + " elements/second"); + + // Limitarlo si vamos a más de una página por segundo... + shouldRequestThumb = speed < visibleItemCount; + } + } + }); + selectedColor = 0xff32b2e1; + // selectedColor = Color.RED; + + // gridView.setBackgroundColor(bgColor); + // gridView.setBackgroundResource(R.drawable.grid_background); + + ia = new ImageAdapter(this); + gridView.setAdapter(ia); + + LoaderManager.enableDebugLogging(false); + getLoaderManager().initLoader(CURSORLOADER_THUMBS, null, this); + getLoaderManager().initLoader(CURSORLOADER_REAL, null, this); + setupHeader(); + updateAcceptButton(); + } + + private void setupHeader() { + // From Roman Nurik's code + // https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW + // Inflate a "Done/Discard" custom action bar view. + LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService( + LAYOUT_INFLATER_SERVICE); + final View customActionBarView = inflater.inflate(R.layout.actionbar_custom_view_done_discard, null); + customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + selectClicked(null); + } + }); + customActionBarView.findViewById(R.id.actionbar_discard).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + + // Show the custom action bar view and hide the normal Home icon and + // title. + final ActionBar actionBar = getActionBar(); + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM + | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE); + actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + } + + private void updateLabel() { + if (freeLabel != null) { + String text = String.format(getString(R.string.free_version_label), maxImages); + freeLabel.setText(text); + if (maxImages == 0) { + freeLabel.setTextColor(Color.RED); + } else { + freeLabel.setTextColor(Color.WHITE); + } + } + } + + public class ImageAdapter extends BaseAdapter { + private final Bitmap mPlaceHolderBitmap; + + public ImageAdapter(Context c) { + Bitmap tmpHolderBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.loading_icon); + mPlaceHolderBitmap = Bitmap.createScaledBitmap(tmpHolderBitmap, colWidth, colWidth, false); + if (tmpHolderBitmap != mPlaceHolderBitmap) { + tmpHolderBitmap.recycle(); + tmpHolderBitmap = null; + } + } + + public int getCount() { + if (imagecursor != null) { + return imagecursor.getCount(); + } else { + return 0; + } + } + + public Object getItem(int position) { + return position; + } + + public long getItemId(int position) { + return position; + } + + // create a new ImageView for each item referenced by the Adapter + public View getView(int pos, View convertView, ViewGroup parent) { + + if (convertView == null) { + convertView = new ImageView(MultiImageChooserActivity.this); + } + + ImageView imageView = (ImageView) convertView; + imageView.setImageBitmap(null); + + final int position = pos; + + if (!imagecursor.moveToPosition(position)) { + return imageView; + } + + if (image_column_index == -1) { + return imageView; + } + + final int id = imagecursor.getInt(image_column_index); + if (isChecked(pos)) { + imageView.setBackgroundColor(selectedColor); + } else { + imageView.setBackgroundColor(Color.TRANSPARENT); + } + if (shouldRequestThumb) { + fetcher.fetch(Integer.valueOf(id), imageView, colWidth); + } + + return imageView; + } + } + + private String getImageName(int position) { + actualimagecursor.moveToPosition(position); + String name = null; + + try { + name = actualimagecursor.getString(actual_image_column_index); + } catch (Exception e) { + return null; + } + return name; + } + + private void setChecked(int position, boolean b) { + checkStatus.put(position, b); + } + + public boolean isChecked(int position) { + boolean ret = checkStatus.get(position); + return ret; + } + + public void cancelClicked(View ignored) { + setResult(RESULT_CANCELED); + finish(); + } + + public void selectClicked(View ignored) { + Intent data = new Intent(); + if (fileNames.isEmpty()) { + this.setResult(RESULT_CANCELED); + } else { + + ArrayList al = new ArrayList(); + al.addAll(fileNames); + Bundle res = new Bundle(); + res.putStringArrayList("MULTIPLEFILENAMES", al); + if (imagecursor != null) { + res.putInt("TOTALFILES", imagecursor.getCount()); + } + + data.putExtras(res); + this.setResult(RESULT_OK, data); + } + Log.d(TAG, "Returning " + fileNames.size() + " items"); + finish(); + } + + @Override + public void onItemClick(AdapterView arg0, View view, int position, long id) { + String name = getImageName(position); + + if (name == null) { + return; + } + boolean isChecked = !isChecked(position); + // PhotoMix.Log("DAVID", "Posicion " + position + " isChecked: " + + // isChecked); + if (!unlimitedImages && maxImages == 0 && isChecked) { + // PhotoMix.Log("DAVID", "Aquí no debería entrar..."); + isChecked = false; + } + + if (isChecked) { + // Solo se resta un slot si hemos introducido un + // filename de verdad... + if (fileNames.add(name)) { + maxImages--; + view.setBackgroundColor(selectedColor); + } + } else { + if (fileNames.remove(name)) { + // Solo incrementa los slots libres si hemos + // "liberado" uno... + maxImages++; + view.setBackgroundColor(Color.TRANSPARENT); + } + } + + setChecked(position, isChecked); + updateAcceptButton(); + updateLabel(); + + } + + private void updateAcceptButton() { + ((TextView) getActionBar().getCustomView().findViewById(R.id.actionbar_done_textview)) + .setEnabled(fileNames.size() != 0); + getActionBar().getCustomView().findViewById(R.id.actionbar_done).setEnabled(fileNames.size() != 0); + + } + + @Override + public Loader onCreateLoader(int cursorID, Bundle arg1) { + CursorLoader cl = null; + + ArrayList img = new ArrayList(); + switch (cursorID) { + + case CURSORLOADER_THUMBS: + img.add(MediaStore.Images.Media._ID); + break; + case CURSORLOADER_REAL: + img.add(MediaStore.Images.Thumbnails.DATA); + break; + default: + break; + } + + cl = new CursorLoader(MultiImageChooserActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + img.toArray(new String[img.size()]), null, null, null); + return cl; + } + + @Override + public void onLoadFinished(Loader loader, Cursor cursor) { + if (cursor == null) { + // NULL cursor. This usually means there's no image database yet.... + return; + } + + switch (loader.getId()) { + case CURSORLOADER_THUMBS: + imagecursor = cursor; + image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID); + ia.notifyDataSetChanged(); + break; + case CURSORLOADER_REAL: + actualimagecursor = cursor; + actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + break; + default: + break; + } + } + + @Override + public void onLoaderReset(Loader loader) { + if (loader.getId() == CURSORLOADER_THUMBS) { + imagecursor = null; + } else if (loader.getId() == CURSORLOADER_REAL) { + actualimagecursor = null; + } + } +} \ No newline at end of file diff --git a/src/Android/com/synconset/ImagePicker/ImagePicker.java b/src/Android/com/synconset/ImagePicker/ImagePicker.java new file mode 100644 index 0000000..46ac0bf --- /dev/null +++ b/src/Android/com/synconset/ImagePicker/ImagePicker.java @@ -0,0 +1,44 @@ +/** + * An Internal Storage Plugin for Cordova/PhoneGap. + */ +package com.synconset; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.io.File; +import java.io.IOException; + +import android.app.Activity; +import android.content.Intent; +import android.util.Log; + +public class ImagePicker extends CordovaPlugin { + + public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + if (action.equals("getPictures")) { + Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); + intent.putExtra("MAX_IMAGES", 20); + + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); + } + } + return true; + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d("PLUGIN", "result code = " + resultCode); + if (resultCode == 1 && data != null) { + ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILNAMES"); + for (int i = 0; i < fileNames.size(); i++) { + Log.d("PLUGIN", fileNames.get(i)); + } + } + } +} From f641587ca4944d03f0e9bef93817b1651ec430ad Mon Sep 17 00:00:00 2001 From: Andrew Stephan Date: Thu, 27 Feb 2014 19:23:26 -0500 Subject: [PATCH 2/5] added android code for resizing images before sending back the urls --- plugin.xml | 5 +- .../src/MultiImageChooserActivity.java | 3 +- .../synconset/ImagePicker/ImagePicker.java | 111 ++++++++++++++++-- 3 files changed, 103 insertions(+), 16 deletions(-) diff --git a/plugin.xml b/plugin.xml index e19ae37..02a32e6 100644 --- a/plugin.xml +++ b/plugin.xml @@ -64,10 +64,7 @@ - - - - + diff --git a/src/Android/Library/src/MultiImageChooserActivity.java b/src/Android/Library/src/MultiImageChooserActivity.java index a7d0b3b..94aadbd 100644 --- a/src/Android/Library/src/MultiImageChooserActivity.java +++ b/src/Android/Library/src/MultiImageChooserActivity.java @@ -146,7 +146,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi } private void setupHeader() { - // From Roman Nurik's code + // From Roman Nkk's code // https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW // Inflate a "Done/Discard" custom action bar view. LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService( @@ -280,7 +280,6 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi if (fileNames.isEmpty()) { this.setResult(RESULT_CANCELED); } else { - ArrayList al = new ArrayList(); al.addAll(fileNames); Bundle res = new Bundle(); diff --git a/src/Android/com/synconset/ImagePicker/ImagePicker.java b/src/Android/com/synconset/ImagePicker/ImagePicker.java index 46ac0bf..c72a0d0 100644 --- a/src/Android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/Android/com/synconset/ImagePicker/ImagePicker.java @@ -1,5 +1,9 @@ /** - * An Internal Storage Plugin for Cordova/PhoneGap. + * An Image Picker Plugin for Cordova/PhoneGap. + * + * The software is open source, MIT Licensed. + * Portions taken from Copyright (C) 2012, webXells GmbH All Rights Reserved. + * */ package com.synconset; @@ -12,19 +16,30 @@ import org.json.JSONObject; import java.util.ArrayList; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import android.app.Activity; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.net.Uri; import android.util.Log; public class ImagePicker extends CordovaPlugin { + public static String TAG = "ImagePicker"; + + private CallbackContext callbackContext; + private JSONObject params; public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + this.callbackContext = callbackContext; + this.params = args.getJSONObject(0); if (action.equals("getPictures")) { Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); - intent.putExtra("MAX_IMAGES", 20); - + intent.putExtra("MAX_IMAGES", this.params.getInt("maximumImagesCount")); if (this.cordova != null) { this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); } @@ -33,12 +48,88 @@ public class ImagePicker extends CordovaPlugin { } public void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d("PLUGIN", "result code = " + resultCode); - if (resultCode == 1 && data != null) { - ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILNAMES"); - for (int i = 0; i < fileNames.size(); i++) { - Log.d("PLUGIN", fileNames.get(i)); - } + if (resultCode == Activity.RESULT_OK && data != null) { + ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); + JSONArray res = new JSONArray(); + try { + for (int i = 0; i < fileNames.size(); i++) { + File file = new File(fileNames.get(i)); + Bitmap bmp = this.getBitmap(file); + int width = bmp.getWidth(); + int height = bmp.getHeight(); + int desiredWidth = this.params.getInt("width"); + int desiredHeight = this.params.getInt("height"); + float widthScale = 1.0f; + float heightScale = 1.0f; + if (desiredWidth > 0 || desiredHeight > 0) { + if (desiredHeight == 0 && desiredWidth < width) { + widthScale = (float)desiredWidth/width; + heightScale = widthScale; + } else if (desiredWidth == 0 && desiredHeight < height) { + heightScale = (float)desiredHeight/height; + widthScale = heightScale; + } else { + if (desiredWidth > 0 && desiredWidth < width) { + widthScale = (float)desiredWidth/width; + } + if (desiredHeight > 0 && desiredHeight < height) { + heightScale = (float)desiredHeight/height; + } + } + } + if (widthScale < 1 || heightScale < 1) { + bmp = this.getResizedBitmap(bmp, widthScale, heightScale); + } + file = this.storeImage(bmp, file.getName()); + res.put(Uri.fromFile(file).toString()); + } + this.callbackContext.success(res); + } catch(IOException e) { + this.callbackContext.error("There was an error importing pictures"); + } catch (JSONException e) { + this.callbackContext.error("There was an error importing pictures"); + } + } else { + this.callbackContext.error("No images selected"); } } -} + + private File storeImage(Bitmap bmp, String fileName) throws JSONException, IOException { + int quality = this.params.getInt("quality"); + int index = fileName.lastIndexOf('.'); + String name = fileName.substring(0, index); + String ext = fileName.substring(index); + File file = File.createTempFile(name, ext); + OutputStream outStream = new FileOutputStream(file); + if (ext.compareToIgnoreCase(".png") == 0) { + bmp.compress(Bitmap.CompressFormat.PNG, quality, outStream); + } else { + bmp.compress(Bitmap.CompressFormat.JPEG, quality, outStream); + } + outStream.flush(); + outStream.close(); + return file; + } + + private Bitmap getResizedBitmap(Bitmap bm, float widthFactor, float heightFactor) { + int width = bm.getWidth(); + int height = bm.getHeight(); + // create a matrix for the manipulation + Matrix matrix = new Matrix(); + // resize the bit map + matrix.postScale(widthFactor, heightFactor); + // recreate the new Bitmap + Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, + matrix, false); + return resizedBitmap; + } + + private Bitmap getBitmap(File file) throws IOException { + Bitmap bmp; + bmp = BitmapFactory.decodeFile(file.getAbsolutePath()); + if (bmp == null) { + throw new IOException("The image file could not be opened."); + } + return bmp; + } +} \ No newline at end of file From e00327dae7e56fcf242034b38003888617980d48 Mon Sep 17 00:00:00 2001 From: Andrew Stephan Date: Fri, 28 Feb 2014 17:30:13 -0500 Subject: [PATCH 3/5] android imagepicker now working. Need to generalize the layout stuff and make it lighter still --- README.md | 31 ++--- .../Library/res/layout/multiselectorgrid.xml | 28 ++--- src/Android/Library/src/ImageFetcher.java | 10 +- .../src/MultiImageChooserActivity.java | 110 +++++++++++------- .../synconset/ImagePicker/ImagePicker.java | 81 ++++++++----- 5 files changed, 154 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index ef43ba9..6237fec 100644 --- a/README.md +++ b/README.md @@ -54,21 +54,22 @@ window.imagePicker.getPictures( ### Options -```javascript -options = { - maximumImagesCount: int, - // max images to be selected, defaults to 15. If this is set to 1, upon - // selection of a single image, the plugin will return it. - width: int, - // width to resize image to (if one of height/width is 0, will resize - // to fit the other while keeping aspect ratio, if both height and width - // are 0, the full size image will be returned) - height: int, - // height to resize image to - quality: int (0-100) - // quality of resized image, defaults to 100 -}; -``` + options = { + // max images to be selected, defaults to 15. If this is set to 1, upon + // selection of a single image, the plugin will return it. + maximumImagesCount: int, + + // max width and height to allow the images to be. Will keep aspect + // ratio no matter what. So if both are 800, the returned image + // will be at most 800 pixels wide and 800 pixels tall. If the width is + // 800 and height 0 the image will be 800 pixels wide if the source + // is at least that wide. + width: int, + height: int, + + // quality of resized image, defaults to 100 + quality: int (0-100) + }; ## License diff --git a/src/Android/Library/res/layout/multiselectorgrid.xml b/src/Android/Library/res/layout/multiselectorgrid.xml index 6e22b5f..01f8e2d 100644 --- a/src/Android/Library/res/layout/multiselectorgrid.xml +++ b/src/Android/Library/res/layout/multiselectorgrid.xml @@ -3,31 +3,21 @@ android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" > - + + \ No newline at end of file diff --git a/src/Android/Library/src/ImageFetcher.java b/src/Android/Library/src/ImageFetcher.java index f384862..a2540bf 100644 --- a/src/Android/Library/src/ImageFetcher.java +++ b/src/Android/Library/src/ImageFetcher.java @@ -118,7 +118,7 @@ public class ImageFetcher { if (bitmapDownloaderTask != null) { Integer bitmapPosition = bitmapDownloaderTask.position; if ((bitmapPosition == null) || (!bitmapPosition.equals(position))) { - Log.d("DAVID", "Canceling..."); + // Log.d("DAVID", "Canceling..."); MediaStore.Images.Thumbnails.cancelThumbnailRequest(imageView.getContext().getContentResolver(), origId, 12345); bitmapDownloaderTask.cancel(true); @@ -180,7 +180,7 @@ public class ImageFetcher { return null; } Bitmap thumb = MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), position, 12345, - MediaStore.Images.Thumbnails.MICRO_KIND, null); + MediaStore.Images.Thumbnails.MINI_KIND, null); if (isCancelled()) { return null; } @@ -197,7 +197,7 @@ public class ImageFetcher { } private void setInvisible() { - Log.d("COLLAGE", "Setting something invisible..."); + // Log.d("COLLAGE", "Setting something invisible..."); if (imageViewReference != null) { final ImageView imageView = imageViewReference.get(); BitmapFetcherTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); @@ -324,7 +324,7 @@ public class ImageFetcher { synchronized (sHardBitmapCache) { final Bitmap bitmap = sHardBitmapCache.get(position); if (bitmap != null) { - Log.d("CACHE ****** ", "Hard hit!"); + // Log.d("CACHE ****** ", "Hard hit!"); // Bitmap found in hard cache // Move element to first position, so that it is removed last return bitmap; @@ -337,7 +337,7 @@ public class ImageFetcher { final Bitmap bitmap = bitmapReference.get(); if (bitmap != null) { // Bitmap found in soft cache - Log.d("CACHE ****** ", "Soft hit!"); + // Log.d("CACHE ****** ", "Soft hit!"); return bitmap; } else { // Soft reference has been Garbage Collected diff --git a/src/Android/Library/src/MultiImageChooserActivity.java b/src/Android/Library/src/MultiImageChooserActivity.java index 94aadbd..5a19146 100644 --- a/src/Android/Library/src/MultiImageChooserActivity.java +++ b/src/Android/Library/src/MultiImageChooserActivity.java @@ -1,3 +1,33 @@ +/* + * Copyright (c) 2012, David Erosa + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDIN G NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE + * + * Code modified by Andrew Stephan for Sync OnSet + * + */ + package com.synconset; import java.util.ArrayList; @@ -59,9 +89,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private SparseBooleanArray checkStatus = new SparseBooleanArray(); - private TextView freeLabel = null; private int maxImages; - private boolean unlimitedImages = false; private GridView gridView; @@ -78,28 +106,17 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT); - unlimitedImages = maxImages == NOLIMIT; - if (!unlimitedImages) { - freeLabel = (TextView) findViewById(R.id.label_images_left); - freeLabel.setVisibility(View.VISIBLE); - updateLabel(); - } - colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH); Display display = getWindowManager().getDefaultDisplay(); - @SuppressWarnings("deprecation") int width = display.getWidth(); - int testColWidth = width / 3; - - if (testColWidth > colWidth) { - colWidth = width / 4; - } + + colWidth = width / 4; // int bgColor = getIntent().getIntExtra("BG_COLOR", Color.BLACK); gridView = (GridView) findViewById(R.id.gridview); - gridView.setColumnWidth(colWidth); + //gridView.setColumnWidth(colWidth); gridView.setOnItemClickListener(this); gridView.setOnScrollListener(new OnScrollListener() { @@ -109,7 +126,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) { - Log.d(TAG, "IDLE - Reload!"); + // Log.d(TAG, "IDLE - Reload!"); shouldRequestThumb = true; ia.notifyDataSetChanged(); } @@ -122,7 +139,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi double speed = 1 / dt * 1000; lastFirstItem = firstVisibleItem; timestamp = System.currentTimeMillis(); - Log.d(TAG, "Speed: " + speed + " elements/second"); + // Log.d(TAG, "Speed: " + speed + " elements/second"); // Limitarlo si vamos a más de una página por segundo... shouldRequestThumb = speed < visibleItemCount; @@ -148,7 +165,22 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private void setupHeader() { // From Roman Nkk's code // https://plus.google.com/113735310430199015092/posts/R49wVvcDoEW - // Inflate a "Done/Discard" custom action bar view. + // Inflate a "Done/Discard" custom action bar view + /* + * 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. + */ LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService( LAYOUT_INFLATER_SERVICE); final View customActionBarView = inflater.inflate(R.layout.actionbar_custom_view_done_discard, null); @@ -174,16 +206,15 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi actionBar.setCustomView(customActionBarView, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } + + public class SquareImageView extends ImageView { + public SquareImageView(Context context) { + super(context); + } - private void updateLabel() { - if (freeLabel != null) { - String text = String.format(getString(R.string.free_version_label), maxImages); - freeLabel.setText(text); - if (maxImages == 0) { - freeLabel.setTextColor(Color.RED); - } else { - freeLabel.setTextColor(Color.WHITE); - } + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, widthMeasureSpec); } } @@ -219,10 +250,12 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi public View getView(int pos, View convertView, ViewGroup parent) { if (convertView == null) { - convertView = new ImageView(MultiImageChooserActivity.this); + ImageView temp = new SquareImageView(MultiImageChooserActivity.this); + temp.setScaleType(ImageView.ScaleType.CENTER_CROP); + convertView = (View)temp; } - ImageView imageView = (ImageView) convertView; + ImageView imageView = (ImageView)convertView; imageView.setImageBitmap(null); final int position = pos; @@ -237,8 +270,10 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi final int id = imagecursor.getInt(image_column_index); if (isChecked(pos)) { + imageView.setImageAlpha(128); imageView.setBackgroundColor(selectedColor); } else { + imageView.setImageAlpha(255); imageView.setBackgroundColor(Color.TRANSPARENT); } if (shouldRequestThumb) { @@ -291,7 +326,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi data.putExtras(res); this.setResult(RESULT_OK, data); } - Log.d(TAG, "Returning " + fileNames.size() + " items"); + // Log.d(TAG, "Returning " + fileNames.size() + " items"); finish(); } @@ -303,18 +338,15 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi return; } boolean isChecked = !isChecked(position); - // PhotoMix.Log("DAVID", "Posicion " + position + " isChecked: " + - // isChecked); - if (!unlimitedImages && maxImages == 0 && isChecked) { - // PhotoMix.Log("DAVID", "Aquí no debería entrar..."); + if (maxImages == 0 && isChecked) { isChecked = false; } if (isChecked) { - // Solo se resta un slot si hemos introducido un - // filename de verdad... if (fileNames.add(name)) { maxImages--; + ImageView imageView = (ImageView)view; + imageView.setImageAlpha(128); view.setBackgroundColor(selectedColor); } } else { @@ -322,14 +354,14 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi // Solo incrementa los slots libres si hemos // "liberado" uno... maxImages++; + ImageView imageView = (ImageView)view; + imageView.setImageAlpha(255); view.setBackgroundColor(Color.TRANSPARENT); } } setChecked(position, isChecked); updateAcceptButton(); - updateLabel(); - } private void updateAcceptButton() { diff --git a/src/Android/com/synconset/ImagePicker/ImagePicker.java b/src/Android/com/synconset/ImagePicker/ImagePicker.java index c72a0d0..f267920 100644 --- a/src/Android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/Android/com/synconset/ImagePicker/ImagePicker.java @@ -1,9 +1,5 @@ /** * An Image Picker Plugin for Cordova/PhoneGap. - * - * The software is open source, MIT Licensed. - * Portions taken from Copyright (C) 2012, webXells GmbH All Rights Reserved. - * */ package com.synconset; @@ -39,7 +35,11 @@ public class ImagePicker extends CordovaPlugin { this.params = args.getJSONObject(0); if (action.equals("getPictures")) { Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); - intent.putExtra("MAX_IMAGES", this.params.getInt("maximumImagesCount")); + int max = 20; + if (this.params.has("maximumImagesCount")) { + max = this.params.getInt("maximumImagesCount"); + } + intent.putExtra("MAX_IMAGES", max); if (this.cordova != null) { this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); } @@ -57,17 +57,22 @@ public class ImagePicker extends CordovaPlugin { Bitmap bmp = this.getBitmap(file); int width = bmp.getWidth(); int height = bmp.getHeight(); - int desiredWidth = this.params.getInt("width"); - int desiredHeight = this.params.getInt("height"); + int desiredWidth = 0; + int desiredHeight = 0; + if (this.params.has("width")) { + desiredWidth = this.params.getInt("width"); + } + if (this.params.has("height")) { + desiredHeight = this.params.getInt("height"); + } float widthScale = 1.0f; float heightScale = 1.0f; + float scale = 1.0f; if (desiredWidth > 0 || desiredHeight > 0) { if (desiredHeight == 0 && desiredWidth < width) { - widthScale = (float)desiredWidth/width; - heightScale = widthScale; + scale = (float)desiredWidth/width; } else if (desiredWidth == 0 && desiredHeight < height) { - heightScale = (float)desiredHeight/height; - widthScale = heightScale; + scale = (float)desiredHeight/height; } else { if (desiredWidth > 0 && desiredWidth < width) { widthScale = (float)desiredWidth/width; @@ -75,10 +80,15 @@ public class ImagePicker extends CordovaPlugin { if (desiredHeight > 0 && desiredHeight < height) { heightScale = (float)desiredHeight/height; } + if (widthScale < heightScale) { + scale = widthScale; + } else { + scale = heightScale; + } } } - if (widthScale < 1 || heightScale < 1) { - bmp = this.getResizedBitmap(bmp, widthScale, heightScale); + if (scale < 1) { + bmp = this.getResizedBitmap(bmp, scale); } file = this.storeImage(bmp, file.getName()); res.put(Uri.fromFile(file).toString()); @@ -89,38 +99,53 @@ public class ImagePicker extends CordovaPlugin { } catch (JSONException e) { this.callbackContext.error("There was an error importing pictures"); } + } else if (resultCode == Activity.RESULT_CANCELED) { + JSONArray res = new JSONArray(); + this.callbackContext.success(res); } else { this.callbackContext.error("No images selected"); } } + + /* + * The following functions are originally from + * https://github.com/raananw/PhoneGap-Image-Resizer + * + * They have been modified by Andrew Stephan for Sync OnSet + * + * The software is open source, MIT Licensed. + * Copyright (C) 2012, webXells GmbH All Rights Reserved. + */ private File storeImage(Bitmap bmp, String fileName) throws JSONException, IOException { - int quality = this.params.getInt("quality"); - int index = fileName.lastIndexOf('.'); - String name = fileName.substring(0, index); - String ext = fileName.substring(index); - File file = File.createTempFile(name, ext); - OutputStream outStream = new FileOutputStream(file); - if (ext.compareToIgnoreCase(".png") == 0) { - bmp.compress(Bitmap.CompressFormat.PNG, quality, outStream); - } else { - bmp.compress(Bitmap.CompressFormat.JPEG, quality, outStream); - } + int quality = 100; + if (this.params.has("quality")) { + quality = this.params.getInt("quality"); + } + int index = fileName.lastIndexOf('.'); + String name = fileName.substring(0, index); + String ext = fileName.substring(index); + File file = File.createTempFile(name, ext); + OutputStream outStream = new FileOutputStream(file); + if (ext.compareToIgnoreCase(".png") == 0) { + bmp.compress(Bitmap.CompressFormat.PNG, quality, outStream); + } else { + bmp.compress(Bitmap.CompressFormat.JPEG, quality, outStream); + } outStream.flush(); outStream.close(); return file; } - private Bitmap getResizedBitmap(Bitmap bm, float widthFactor, float heightFactor) { + private Bitmap getResizedBitmap(Bitmap bm, float factor) { int width = bm.getWidth(); int height = bm.getHeight(); // create a matrix for the manipulation Matrix matrix = new Matrix(); // resize the bit map - matrix.postScale(widthFactor, heightFactor); + matrix.postScale(factor, factor); // recreate the new Bitmap - Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, - matrix, false); + Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false); return resizedBitmap; } From 41c6bfd4d645bf8d15177bc12128091fa42e4356 Mon Sep 17 00:00:00 2001 From: Andrew Stephan Date: Mon, 3 Mar 2014 13:31:06 -0500 Subject: [PATCH 4/5] sorted images correctly, made the theme light, and added an alert saying when no more pictures can be added --- plugin.xml | 4 +-- .../Library/res/anim/image_pop_in.xml | 0 .../Library/res/drawable-hdpi/icon.png | Bin .../Library/res/drawable-hdpi/image_bg.9.png | Bin .../res/drawable-hdpi/loading_icon.png | Bin .../Library/res/drawable-ldpi/icon.png | Bin .../drawable-mdpi/ic_action_discard_dark.png | Bin .../drawable-mdpi/ic_action_discard_light.png | Bin .../res/drawable-mdpi/ic_action_done_dark.png | Bin .../drawable-mdpi/ic_action_done_light.png | Bin .../Library/res/drawable-mdpi/ic_launcher.png | Bin .../Library/res/drawable-mdpi/icon.png | Bin .../drawable-xhdpi/ic_action_discard_dark.png | Bin .../ic_action_discard_light.png | Bin .../drawable-xhdpi/ic_action_done_dark.png | Bin .../drawable-xhdpi/ic_action_done_light.png | Bin .../res/drawable-xhdpi/ic_launcher.png | Bin .../Library/res/drawable/grid_background.xml | 0 .../actionbar_custom_view_done_discard.xml | 0 .../res/layout/actionbar_discard_button.xml | 2 +- .../res/layout/actionbar_done_button.xml | 2 +- .../Library/res/layout/multiselectorgrid.xml | 0 .../multiimagechooser_strings_de.xml | 0 .../multiimagechooser_strings_es.xml | 0 .../multiimagechooser_strings_fr.xml | 0 .../multiimagechooser_strings_hu.xml | 0 .../multiimagechooser_strings_ja.xml | 0 .../multiimagechooser_strings_ko.xml | 0 .../values/multiimagechooser_strings_en.xml | 0 .../Library/res/values/themes.xml | 0 .../Library/src/ImageFetcher.java | 0 .../src/MultiImageChooserActivity.java | 28 ++++++++++++++---- .../synconset/ImagePicker/ImagePicker.java | 0 33 files changed, 27 insertions(+), 9 deletions(-) rename src/{Android => android}/Library/res/anim/image_pop_in.xml (100%) rename src/{Android => android}/Library/res/drawable-hdpi/icon.png (100%) rename src/{Android => android}/Library/res/drawable-hdpi/image_bg.9.png (100%) rename src/{Android => android}/Library/res/drawable-hdpi/loading_icon.png (100%) rename src/{Android => android}/Library/res/drawable-ldpi/icon.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/ic_action_discard_dark.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/ic_action_discard_light.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/ic_action_done_dark.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/ic_action_done_light.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/ic_launcher.png (100%) rename src/{Android => android}/Library/res/drawable-mdpi/icon.png (100%) rename src/{Android => android}/Library/res/drawable-xhdpi/ic_action_discard_dark.png (100%) rename src/{Android => android}/Library/res/drawable-xhdpi/ic_action_discard_light.png (100%) rename src/{Android => android}/Library/res/drawable-xhdpi/ic_action_done_dark.png (100%) rename src/{Android => android}/Library/res/drawable-xhdpi/ic_action_done_light.png (100%) rename src/{Android => android}/Library/res/drawable-xhdpi/ic_launcher.png (100%) rename src/{Android => android}/Library/res/drawable/grid_background.xml (100%) rename src/{Android => android}/Library/res/layout/actionbar_custom_view_done_discard.xml (100%) rename src/{Android => android}/Library/res/layout/actionbar_discard_button.xml (95%) rename src/{Android => android}/Library/res/layout/actionbar_done_button.xml (95%) rename src/{Android => android}/Library/res/layout/multiselectorgrid.xml (100%) rename src/{Android => android}/Library/res/values-de/multiimagechooser_strings_de.xml (100%) rename src/{Android => android}/Library/res/values-es/multiimagechooser_strings_es.xml (100%) rename src/{Android => android}/Library/res/values-fr/multiimagechooser_strings_fr.xml (100%) rename src/{Android => android}/Library/res/values-hu/multiimagechooser_strings_hu.xml (100%) rename src/{Android => android}/Library/res/values-ja/multiimagechooser_strings_ja.xml (100%) rename src/{Android => android}/Library/res/values-ko/multiimagechooser_strings_ko.xml (100%) rename src/{Android => android}/Library/res/values/multiimagechooser_strings_en.xml (100%) rename src/{Android => android}/Library/res/values/themes.xml (100%) rename src/{Android => android}/Library/src/ImageFetcher.java (100%) rename src/{Android => android}/Library/src/MultiImageChooserActivity.java (93%) rename src/{Android => android}/com/synconset/ImagePicker/ImagePicker.java (100%) diff --git a/plugin.xml b/plugin.xml index 02a32e6..5202947 100644 --- a/plugin.xml +++ b/plugin.xml @@ -63,12 +63,12 @@ - + - + diff --git a/src/Android/Library/res/anim/image_pop_in.xml b/src/android/Library/res/anim/image_pop_in.xml similarity index 100% rename from src/Android/Library/res/anim/image_pop_in.xml rename to src/android/Library/res/anim/image_pop_in.xml diff --git a/src/Android/Library/res/drawable-hdpi/icon.png b/src/android/Library/res/drawable-hdpi/icon.png similarity index 100% rename from src/Android/Library/res/drawable-hdpi/icon.png rename to src/android/Library/res/drawable-hdpi/icon.png diff --git a/src/Android/Library/res/drawable-hdpi/image_bg.9.png b/src/android/Library/res/drawable-hdpi/image_bg.9.png similarity index 100% rename from src/Android/Library/res/drawable-hdpi/image_bg.9.png rename to src/android/Library/res/drawable-hdpi/image_bg.9.png diff --git a/src/Android/Library/res/drawable-hdpi/loading_icon.png b/src/android/Library/res/drawable-hdpi/loading_icon.png similarity index 100% rename from src/Android/Library/res/drawable-hdpi/loading_icon.png rename to src/android/Library/res/drawable-hdpi/loading_icon.png diff --git a/src/Android/Library/res/drawable-ldpi/icon.png b/src/android/Library/res/drawable-ldpi/icon.png similarity index 100% rename from src/Android/Library/res/drawable-ldpi/icon.png rename to src/android/Library/res/drawable-ldpi/icon.png diff --git a/src/Android/Library/res/drawable-mdpi/ic_action_discard_dark.png b/src/android/Library/res/drawable-mdpi/ic_action_discard_dark.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/ic_action_discard_dark.png rename to src/android/Library/res/drawable-mdpi/ic_action_discard_dark.png diff --git a/src/Android/Library/res/drawable-mdpi/ic_action_discard_light.png b/src/android/Library/res/drawable-mdpi/ic_action_discard_light.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/ic_action_discard_light.png rename to src/android/Library/res/drawable-mdpi/ic_action_discard_light.png diff --git a/src/Android/Library/res/drawable-mdpi/ic_action_done_dark.png b/src/android/Library/res/drawable-mdpi/ic_action_done_dark.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/ic_action_done_dark.png rename to src/android/Library/res/drawable-mdpi/ic_action_done_dark.png diff --git a/src/Android/Library/res/drawable-mdpi/ic_action_done_light.png b/src/android/Library/res/drawable-mdpi/ic_action_done_light.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/ic_action_done_light.png rename to src/android/Library/res/drawable-mdpi/ic_action_done_light.png diff --git a/src/Android/Library/res/drawable-mdpi/ic_launcher.png b/src/android/Library/res/drawable-mdpi/ic_launcher.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/ic_launcher.png rename to src/android/Library/res/drawable-mdpi/ic_launcher.png diff --git a/src/Android/Library/res/drawable-mdpi/icon.png b/src/android/Library/res/drawable-mdpi/icon.png similarity index 100% rename from src/Android/Library/res/drawable-mdpi/icon.png rename to src/android/Library/res/drawable-mdpi/icon.png diff --git a/src/Android/Library/res/drawable-xhdpi/ic_action_discard_dark.png b/src/android/Library/res/drawable-xhdpi/ic_action_discard_dark.png similarity index 100% rename from src/Android/Library/res/drawable-xhdpi/ic_action_discard_dark.png rename to src/android/Library/res/drawable-xhdpi/ic_action_discard_dark.png diff --git a/src/Android/Library/res/drawable-xhdpi/ic_action_discard_light.png b/src/android/Library/res/drawable-xhdpi/ic_action_discard_light.png similarity index 100% rename from src/Android/Library/res/drawable-xhdpi/ic_action_discard_light.png rename to src/android/Library/res/drawable-xhdpi/ic_action_discard_light.png diff --git a/src/Android/Library/res/drawable-xhdpi/ic_action_done_dark.png b/src/android/Library/res/drawable-xhdpi/ic_action_done_dark.png similarity index 100% rename from src/Android/Library/res/drawable-xhdpi/ic_action_done_dark.png rename to src/android/Library/res/drawable-xhdpi/ic_action_done_dark.png diff --git a/src/Android/Library/res/drawable-xhdpi/ic_action_done_light.png b/src/android/Library/res/drawable-xhdpi/ic_action_done_light.png similarity index 100% rename from src/Android/Library/res/drawable-xhdpi/ic_action_done_light.png rename to src/android/Library/res/drawable-xhdpi/ic_action_done_light.png diff --git a/src/Android/Library/res/drawable-xhdpi/ic_launcher.png b/src/android/Library/res/drawable-xhdpi/ic_launcher.png similarity index 100% rename from src/Android/Library/res/drawable-xhdpi/ic_launcher.png rename to src/android/Library/res/drawable-xhdpi/ic_launcher.png diff --git a/src/Android/Library/res/drawable/grid_background.xml b/src/android/Library/res/drawable/grid_background.xml similarity index 100% rename from src/Android/Library/res/drawable/grid_background.xml rename to src/android/Library/res/drawable/grid_background.xml diff --git a/src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml b/src/android/Library/res/layout/actionbar_custom_view_done_discard.xml similarity index 100% rename from src/Android/Library/res/layout/actionbar_custom_view_done_discard.xml rename to src/android/Library/res/layout/actionbar_custom_view_done_discard.xml diff --git a/src/Android/Library/res/layout/actionbar_discard_button.xml b/src/android/Library/res/layout/actionbar_discard_button.xml similarity index 95% rename from src/Android/Library/res/layout/actionbar_discard_button.xml rename to src/android/Library/res/layout/actionbar_discard_button.xml index 3e72461..bbe6321 100644 --- a/src/Android/Library/res/layout/actionbar_discard_button.xml +++ b/src/android/Library/res/layout/actionbar_discard_button.xml @@ -20,7 +20,7 @@ android:layout_width="0dp" style="?android:actionButtonStyle" > diff --git a/src/Android/Library/res/layout/multiselectorgrid.xml b/src/android/Library/res/layout/multiselectorgrid.xml similarity index 100% rename from src/Android/Library/res/layout/multiselectorgrid.xml rename to src/android/Library/res/layout/multiselectorgrid.xml diff --git a/src/Android/Library/res/values-de/multiimagechooser_strings_de.xml b/src/android/Library/res/values-de/multiimagechooser_strings_de.xml similarity index 100% rename from src/Android/Library/res/values-de/multiimagechooser_strings_de.xml rename to src/android/Library/res/values-de/multiimagechooser_strings_de.xml diff --git a/src/Android/Library/res/values-es/multiimagechooser_strings_es.xml b/src/android/Library/res/values-es/multiimagechooser_strings_es.xml similarity index 100% rename from src/Android/Library/res/values-es/multiimagechooser_strings_es.xml rename to src/android/Library/res/values-es/multiimagechooser_strings_es.xml diff --git a/src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml b/src/android/Library/res/values-fr/multiimagechooser_strings_fr.xml similarity index 100% rename from src/Android/Library/res/values-fr/multiimagechooser_strings_fr.xml rename to src/android/Library/res/values-fr/multiimagechooser_strings_fr.xml diff --git a/src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml b/src/android/Library/res/values-hu/multiimagechooser_strings_hu.xml similarity index 100% rename from src/Android/Library/res/values-hu/multiimagechooser_strings_hu.xml rename to src/android/Library/res/values-hu/multiimagechooser_strings_hu.xml diff --git a/src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml b/src/android/Library/res/values-ja/multiimagechooser_strings_ja.xml similarity index 100% rename from src/Android/Library/res/values-ja/multiimagechooser_strings_ja.xml rename to src/android/Library/res/values-ja/multiimagechooser_strings_ja.xml diff --git a/src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml b/src/android/Library/res/values-ko/multiimagechooser_strings_ko.xml similarity index 100% rename from src/Android/Library/res/values-ko/multiimagechooser_strings_ko.xml rename to src/android/Library/res/values-ko/multiimagechooser_strings_ko.xml diff --git a/src/Android/Library/res/values/multiimagechooser_strings_en.xml b/src/android/Library/res/values/multiimagechooser_strings_en.xml similarity index 100% rename from src/Android/Library/res/values/multiimagechooser_strings_en.xml rename to src/android/Library/res/values/multiimagechooser_strings_en.xml diff --git a/src/Android/Library/res/values/themes.xml b/src/android/Library/res/values/themes.xml similarity index 100% rename from src/Android/Library/res/values/themes.xml rename to src/android/Library/res/values/themes.xml diff --git a/src/Android/Library/src/ImageFetcher.java b/src/android/Library/src/ImageFetcher.java similarity index 100% rename from src/Android/Library/src/ImageFetcher.java rename to src/android/Library/src/ImageFetcher.java diff --git a/src/Android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java similarity index 93% rename from src/Android/Library/src/MultiImageChooserActivity.java rename to src/android/Library/src/MultiImageChooserActivity.java index 5a19146..e48dd5e 100644 --- a/src/Android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -37,9 +37,11 @@ import java.util.Set; import com.wymsee.apps.synconset.R; import android.app.Activity; import android.app.ActionBar; +import android.app.AlertDialog; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; +import android.content.DialogInterface; import android.content.Intent; import android.content.Loader; import android.database.Cursor; @@ -90,6 +92,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private SparseBooleanArray checkStatus = new SparseBooleanArray(); private int maxImages; + private int maxImageCount; private GridView gridView; @@ -105,6 +108,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi fileNames.clear(); maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT); + maxImageCount = maxImages; colWidth = getIntent().getIntExtra(COL_WIDTH_KEY, DEFAULT_COLUMN_WIDTH); @@ -340,14 +344,28 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi boolean isChecked = !isChecked(position); if (maxImages == 0 && isChecked) { isChecked = false; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Maximum " + maxImageCount + " Photos"); + builder.setMessage("You can only select " + maxImageCount + " photos at a time."); + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); } if (isChecked) { if (fileNames.add(name)) { - maxImages--; - ImageView imageView = (ImageView)view; - imageView.setImageAlpha(128); - view.setBackgroundColor(selectedColor); + if (maxImageCount == 1) { + this.selectClicked(null); + } else { + maxImages--; + ImageView imageView = (ImageView)view; + imageView.setImageAlpha(128); + view.setBackgroundColor(selectedColor); + } } } else { if (fileNames.remove(name)) { @@ -389,7 +407,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi } cl = new CursorLoader(MultiImageChooserActivity.this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - img.toArray(new String[img.size()]), null, null, null); + img.toArray(new String[img.size()]), null, null, "DATE_MODIFIED DESC"); return cl; } diff --git a/src/Android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java similarity index 100% rename from src/Android/com/synconset/ImagePicker/ImagePicker.java rename to src/android/com/synconset/ImagePicker/ImagePicker.java From d6bf3d1c10ca46d925c262c212f9ac8be96e7c60 Mon Sep 17 00:00:00 2001 From: Andrew Stephan Date: Mon, 3 Mar 2014 14:14:36 -0500 Subject: [PATCH 5/5] plugin should now work when added to any project --- plugin.xml | 1 + .../src/MultiImageChooserActivity.java | 22 +++++---- .../com/synconset/ImagePicker/FakeR.java | 48 +++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 src/android/com/synconset/ImagePicker/FakeR.java diff --git a/plugin.xml b/plugin.xml index 5202947..7152130 100644 --- a/plugin.xml +++ b/plugin.xml @@ -69,6 +69,7 @@ + diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index e48dd5e..ca46f1f 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -34,7 +34,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -import com.wymsee.apps.synconset.R; +import com.synconset.FakeR; import android.app.Activity; import android.app.ActionBar; import android.app.AlertDialog; @@ -100,11 +100,14 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private int selectedColor = Color.GREEN; private boolean shouldRequestThumb = true; + + private FakeR fakeR; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.multiselectorgrid); + fakeR = new FakeR(this); + setContentView(fakeR.getId("layout", "multiselectorgrid")); fileNames.clear(); maxImages = getIntent().getIntExtra(MAX_IMAGES_KEY, NOLIMIT); @@ -118,8 +121,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi colWidth = width / 4; // int bgColor = getIntent().getIntExtra("BG_COLOR", Color.BLACK); - - gridView = (GridView) findViewById(R.id.gridview); + gridView = (GridView) findViewById(fakeR.getId("id", "gridview")); //gridView.setColumnWidth(colWidth); gridView.setOnItemClickListener(this); gridView.setOnScrollListener(new OnScrollListener() { @@ -187,15 +189,15 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi */ LayoutInflater inflater = (LayoutInflater) getActionBar().getThemedContext().getSystemService( LAYOUT_INFLATER_SERVICE); - final View customActionBarView = inflater.inflate(R.layout.actionbar_custom_view_done_discard, null); - customActionBarView.findViewById(R.id.actionbar_done).setOnClickListener(new View.OnClickListener() { + final View customActionBarView = inflater.inflate(fakeR.getId("layout", "actionbar_custom_view_done_discard"), null); + customActionBarView.findViewById(fakeR.getId("id", "actionbar_done")).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // "Done" selectClicked(null); } }); - customActionBarView.findViewById(R.id.actionbar_discard).setOnClickListener(new View.OnClickListener() { + customActionBarView.findViewById(fakeR.getId("id", "actionbar_discard")).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); @@ -226,7 +228,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private final Bitmap mPlaceHolderBitmap; public ImageAdapter(Context c) { - Bitmap tmpHolderBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.loading_icon); + Bitmap tmpHolderBitmap = BitmapFactory.decodeResource(getResources(), fakeR.getId("drawable", "loading_icon")); mPlaceHolderBitmap = Bitmap.createScaledBitmap(tmpHolderBitmap, colWidth, colWidth, false); if (tmpHolderBitmap != mPlaceHolderBitmap) { tmpHolderBitmap.recycle(); @@ -383,9 +385,9 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi } private void updateAcceptButton() { - ((TextView) getActionBar().getCustomView().findViewById(R.id.actionbar_done_textview)) + ((TextView) getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done_textview"))) .setEnabled(fileNames.size() != 0); - getActionBar().getCustomView().findViewById(R.id.actionbar_done).setEnabled(fileNames.size() != 0); + getActionBar().getCustomView().findViewById(fakeR.getId("id", "actionbar_done")).setEnabled(fileNames.size() != 0); } diff --git a/src/android/com/synconset/ImagePicker/FakeR.java b/src/android/com/synconset/ImagePicker/FakeR.java new file mode 100644 index 0000000..465b64f --- /dev/null +++ b/src/android/com/synconset/ImagePicker/FakeR.java @@ -0,0 +1,48 @@ +/* +The MIT License + +Copyright (c) 2010 Matt Kane + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Code taken from: https://github.com/wildabeast/BarcodeScanner +*/ +package com.synconset; + +import android.app.Activity; +import android.content.Context; + +/** + * R replacement for PhoneGap Build. + * + * ([^.\w])R\.(\w+)\.(\w+) + * $1fakeR("$2", "$3") + * + * @author Maciej Nux Jaros + */ +public class FakeR { + private Context context; + private String packageName; + + public FakeR(Activity activity) { + context = activity.getApplicationContext(); + packageName = context.getPackageName(); + } + + public FakeR(Context context) { + this.context = context; + packageName = context.getPackageName(); + } + + public int getId(String group, String key) { + return context.getResources().getIdentifier(key, group, packageName); + } + + public static int getId(Context context, String group, String key) { + return context.getResources().getIdentifier(key, group, context.getPackageName()); + } +}