diff --git a/android/dist/com.mykingdom.mupdf-android-1.4.zip b/android/dist/com.mykingdom.mupdf-android-1.4.zip deleted file mode 100644 index ec5308c..0000000 Binary files a/android/dist/com.mykingdom.mupdf-android-1.4.zip and /dev/null differ diff --git a/android/dist/mupdf.jar b/android/dist/mupdf.jar index 0723953..0cb686b 100644 Binary files a/android/dist/mupdf.jar and b/android/dist/mupdf.jar differ diff --git a/android/example/app.js b/android/example/app.js index 9cd7c3f..5a9cab2 100644 --- a/android/example/app.js +++ b/android/example/app.js @@ -18,7 +18,14 @@ if (!file.exists()) { console.log(">>EXISTS>>>" + file.exists()); -var pdfReader = READER_MODULE.createPDFReader({ +var processDialog = Ti.UI.Android.createProgressIndicator({ + message : 'Searching...', + location : Ti.UI.Android.PROGRESS_INDICATOR_DIALOG, + type : Ti.UI.Android.PROGRESS_INDICATOR_INDETERMINANT, + cancelable : false +}); + +var pdfReader = READER_MODULE.createView({ file : file }); @@ -60,13 +67,17 @@ win.addEventListener("open", function(e) { }); searchItem.addEventListener("click", function(e) { var toast = Ti.UI.createNotification({ - message : "Search for the keyword 'for' in the entire pdf", + message : "Search for the total occurences of keyword 'for' in the entire pdf. Note : Touch events will be disabled during search", duration : Ti.UI.NOTIFICATION_DURATION_LONG }); toast.show(); count = 0; + processDialog.show(); + pdfReader.touchEnabled = false; pdfReader.onSearch(searchResult); - pdfReader.search("for", 0); + //start search from page no. 1. + //third parameter is optional, defaults to false. Disable the rendering of the search. If true the page will be rendered with results highlighted + pdfReader.search("for", 1, false); }); var previousItem = e.menu.add({ title : "Previous", @@ -88,7 +99,7 @@ win.addEventListener("open", function(e) { }); searchPreviousItem.addEventListener("click", function(e) { pdfReader.onSearch(logSearch); - pdfReader.search("for", -1); + pdfReader.search("for", pdfReader.getCurrentPage() - 1, true); }); var searchNextItem = e.menu.add({ title : "Search Next", @@ -96,45 +107,44 @@ win.addEventListener("open", function(e) { }); searchNextItem.addEventListener("click", function(e) { pdfReader.onSearch(logSearch); - pdfReader.search("for", 1); + pdfReader.search("for", pdfReader.getCurrentPage() + 1, true); }); var toggleHightLight = e.menu.add({ title : "Toggle highlight", showAsAction : Ti.Android.SHOW_AS_ACTION_IF_ROOM }); toggleHightLight.addEventListener("click", function(e) { - enabled = !enabled; - pdfReader.setHighlightColor( enabled ? "#0000FF" : "transparent"); + pdfReader.setHighlightColor( enabled ? "#500000FF" : "transparent"); pdfReader.onSearch(logSearch); - pdfReader.search("for", 0); + //search and render results for the current page + pdfReader.search("for", pdfReader.getCurrentPage(), true); + enabled = !enabled; }); }; activity.invalidateOptionsMenu(); }); +function logSearch(evt) { + console.log(evt); +} + function searchResult(result) { console.log(result); - if (count == 0 && result.error) { - if (result.code == READER_MODULE.ERROR_TEXT_NOT_FOUND) { - alert("No matches found"); - } else if (result.code == READER_MODULE.ERROR_NO_FURTHER_OCCURRENCES_FOUND) { - alert("No more occurrences on the given direction"); - } - return; - } count += result.count; if (result.success && result.currentPage < pdfReader.getPageCount()) { - pdfReader.search("for", 1); + // search for next page until end of the pdf + pdfReader.search("for", result.currentPage + 1); } else { - pdfReader.setCurrentPage(1); - alert("Total occurence : " + count); + processDialog.hide(); + pdfReader.touchEnabled = true; + if (count == 0) { + alert("No matches found"); + } else { + alert("Total occurence : " + count); + } } } -function logSearch(evt) { - console.log(evt); -} - Ti.Gesture.addEventListener("orientationchange", function() { pdfReader.setCurrentPage(pdfReader.getCurrentPage()); }); diff --git a/android/libs/armeabi-v7a/libcom.mykingdom.mupdf.so b/android/libs/armeabi-v7a/libcom.mykingdom.mupdf.so index 2c79a5b..329d45c 100644 Binary files a/android/libs/armeabi-v7a/libcom.mykingdom.mupdf.so and b/android/libs/armeabi-v7a/libcom.mykingdom.mupdf.so differ diff --git a/android/libs/armeabi-v7a/libmupdf.so b/android/libs/armeabi-v7a/libmupdf.so old mode 100644 new mode 100755 index ab915b4..11640ee Binary files a/android/libs/armeabi-v7a/libmupdf.so and b/android/libs/armeabi-v7a/libmupdf.so differ diff --git a/android/libs/armeabi/libcom.mykingdom.mupdf.so b/android/libs/armeabi/libcom.mykingdom.mupdf.so index 52d601d..f1788d1 100644 Binary files a/android/libs/armeabi/libcom.mykingdom.mupdf.so and b/android/libs/armeabi/libcom.mykingdom.mupdf.so differ diff --git a/android/libs/armeabi/libmupdf.so b/android/libs/armeabi/libmupdf.so old mode 100644 new mode 100755 index c3f368a..0fa908d Binary files a/android/libs/armeabi/libmupdf.so and b/android/libs/armeabi/libmupdf.so differ diff --git a/android/libs/x86/libcom.mykingdom.mupdf.so b/android/libs/x86/libcom.mykingdom.mupdf.so index 99af5df..692c50a 100644 Binary files a/android/libs/x86/libcom.mykingdom.mupdf.so and b/android/libs/x86/libcom.mykingdom.mupdf.so differ diff --git a/android/libs/x86/libmupdf.so b/android/libs/x86/libmupdf.so old mode 100644 new mode 100755 index a4ea226..a3b043f Binary files a/android/libs/x86/libmupdf.so and b/android/libs/x86/libmupdf.so differ diff --git a/android/manifest b/android/manifest index b5bcc6d..f6d212d 100644 --- a/android/manifest +++ b/android/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 1.4 +version: 1.5 apiversion: 2 architectures: armeabi armeabi-v7a x86 description: mupdf diff --git a/android/platform/android/res/anim/info.xml b/android/platform/android/res/anim/info.xml deleted file mode 100644 index cd7bff2..0000000 --- a/android/platform/android/res/anim/info.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/android/platform/android/res/animator/info.xml b/android/platform/android/res/animator/info.xml new file mode 100644 index 0000000..9085a9e --- /dev/null +++ b/android/platform/android/res/animator/info.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/android/platform/android/res/drawable-hdpi/darkdenim3.png b/android/platform/android/res/drawable-hdpi/darkdenim3.png deleted file mode 100644 index be532f6..0000000 Binary files a/android/platform/android/res/drawable-hdpi/darkdenim3.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_action_search.png b/android/platform/android/res/drawable-hdpi/ic_action_search.png deleted file mode 100644 index 67de12d..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_action_search.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_empty.png b/android/platform/android/res/drawable-hdpi/ic_empty.png deleted file mode 100644 index d6dc789..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_empty.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_error.png b/android/platform/android/res/drawable-hdpi/ic_error.png deleted file mode 100644 index c26bcea..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_error.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_refresh.png b/android/platform/android/res/drawable-hdpi/ic_refresh.png deleted file mode 100644 index 08c32e0..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_refresh.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_refresh_inverse.png b/android/platform/android/res/drawable-hdpi/ic_refresh_inverse.png deleted file mode 100644 index 9ab1d38..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_refresh_inverse.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_search.png b/android/platform/android/res/drawable-hdpi/ic_search.png deleted file mode 100644 index 59de344..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_search.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_search_inverse.png b/android/platform/android/res/drawable-hdpi/ic_search_inverse.png deleted file mode 100644 index eb090a6..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_search_inverse.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/ic_stub.png b/android/platform/android/res/drawable-hdpi/ic_stub.png deleted file mode 100644 index 99bb7eb..0000000 Binary files a/android/platform/android/res/drawable-hdpi/ic_stub.png and /dev/null differ diff --git a/android/platform/android/res/drawable-hdpi/icon.png b/android/platform/android/res/drawable-hdpi/icon.png new file mode 100644 index 0000000..4f47347 Binary files /dev/null and b/android/platform/android/res/drawable-hdpi/icon.png differ diff --git a/android/platform/android/res/drawable-hdpi/slider.xml b/android/platform/android/res/drawable-hdpi/slider.xml deleted file mode 100644 index bf4a75d..0000000 --- a/android/platform/android/res/drawable-hdpi/slider.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/android/platform/android/res/drawable-ldpi/ic_share.png b/android/platform/android/res/drawable-ldpi/ic_share.png new file mode 100644 index 0000000..05fbe31 Binary files /dev/null and b/android/platform/android/res/drawable-ldpi/ic_share.png differ diff --git a/android/platform/android/res/drawable-ldpi/icon.png b/android/platform/android/res/drawable-ldpi/icon.png new file mode 100644 index 0000000..82655e7 Binary files /dev/null and b/android/platform/android/res/drawable-ldpi/icon.png differ diff --git a/android/platform/android/res/drawable-mdpi/ic_refresh.png b/android/platform/android/res/drawable-mdpi/ic_refresh.png deleted file mode 100644 index 55c43c3..0000000 Binary files a/android/platform/android/res/drawable-mdpi/ic_refresh.png and /dev/null differ diff --git a/android/platform/android/res/drawable-mdpi/ic_refresh_inverse.png b/android/platform/android/res/drawable-mdpi/ic_refresh_inverse.png deleted file mode 100644 index d5736ec..0000000 Binary files a/android/platform/android/res/drawable-mdpi/ic_refresh_inverse.png and /dev/null differ diff --git a/android/platform/android/res/drawable-mdpi/ic_search.png b/android/platform/android/res/drawable-mdpi/ic_search.png deleted file mode 100644 index 75339d9..0000000 Binary files a/android/platform/android/res/drawable-mdpi/ic_search.png and /dev/null differ diff --git a/android/platform/android/res/drawable-mdpi/ic_search_inverse.png b/android/platform/android/res/drawable-mdpi/ic_search_inverse.png deleted file mode 100644 index b462c32..0000000 Binary files a/android/platform/android/res/drawable-mdpi/ic_search_inverse.png and /dev/null differ diff --git a/android/platform/android/res/drawable-mdpi/ic_share.png b/android/platform/android/res/drawable-mdpi/ic_share.png new file mode 100644 index 0000000..cae51b6 Binary files /dev/null and b/android/platform/android/res/drawable-mdpi/ic_share.png differ diff --git a/android/platform/android/res/drawable-mdpi/icon.png b/android/platform/android/res/drawable-mdpi/icon.png new file mode 100644 index 0000000..e05de27 Binary files /dev/null and b/android/platform/android/res/drawable-mdpi/icon.png differ diff --git a/android/platform/android/res/drawable-xhdpi/icon.png b/android/platform/android/res/drawable-xhdpi/icon.png new file mode 100644 index 0000000..0995b78 Binary files /dev/null and b/android/platform/android/res/drawable-xhdpi/icon.png differ diff --git a/android/platform/android/res/drawable/busy.xml b/android/platform/android/res/drawable/busy.xml index 5ac0ce7..f7f42a4 100644 --- a/android/platform/android/res/drawable/busy.xml +++ b/android/platform/android/res/drawable/busy.xml @@ -1,10 +1,10 @@ - - - + android:shape="rectangle"> + + + diff --git a/android/platform/android/res/drawable/button.xml b/android/platform/android/res/drawable/button.xml index 3b87ccb..0a9bcd5 100644 --- a/android/platform/android/res/drawable/button.xml +++ b/android/platform/android/res/drawable/button.xml @@ -1,23 +1,23 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/android/platform/android/res/drawable/page_num.xml b/android/platform/android/res/drawable/page_num.xml index 533579e..8d50df8 100644 --- a/android/platform/android/res/drawable/page_num.xml +++ b/android/platform/android/res/drawable/page_num.xml @@ -1,9 +1,9 @@ - - - + + + diff --git a/android/platform/android/res/drawable/search.xml b/android/platform/android/res/drawable/search.xml index d689c46..4fc5883 100644 --- a/android/platform/android/res/drawable/search.xml +++ b/android/platform/android/res/drawable/search.xml @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/android/platform/android/res/drawable/seek_progress.xml b/android/platform/android/res/drawable/seek_progress.xml index 2722ed0..328139c 100644 --- a/android/platform/android/res/drawable/seek_progress.xml +++ b/android/platform/android/res/drawable/seek_progress.xml @@ -1,6 +1,6 @@ - + android:shape="line" > + diff --git a/android/platform/android/res/drawable/seek_thumb.xml b/android/platform/android/res/drawable/seek_thumb.xml index 518aa58..e3a9bad 100644 --- a/android/platform/android/res/drawable/seek_thumb.xml +++ b/android/platform/android/res/drawable/seek_thumb.xml @@ -1,7 +1,7 @@ - - + android:shape="oval" > + + diff --git a/android/platform/android/res/drawable/tiled_background.xml b/android/platform/android/res/drawable/tiled_background.xml deleted file mode 100644 index a823397..0000000 --- a/android/platform/android/res/drawable/tiled_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/android/platform/android/res/layout/buttons.xml b/android/platform/android/res/layout/buttons.xml index 23336c3..98eddc2 100644 --- a/android/platform/android/res/layout/buttons.xml +++ b/android/platform/android/res/layout/buttons.xml @@ -1,187 +1,385 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" > - + - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:src="@drawable/ic_more" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + android:id="@+id/pageNumber" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_above="@+id/pageSlider" + android:layout_centerHorizontal="true" + android:layout_marginBottom="16dp" + android:background="@drawable/page_num" + android:textColor="#FFFFFF" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + + diff --git a/android/platform/android/res/layout/main.xml b/android/platform/android/res/layout/main.xml new file mode 100644 index 0000000..50b4746 --- /dev/null +++ b/android/platform/android/res/layout/main.xml @@ -0,0 +1,5 @@ + + diff --git a/android/platform/android/res/layout/outline_entry.xml b/android/platform/android/res/layout/outline_entry.xml new file mode 100644 index 0000000..ea7912e --- /dev/null +++ b/android/platform/android/res/layout/outline_entry.xml @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/android/platform/android/res/layout/picker_entry.xml b/android/platform/android/res/layout/picker_entry.xml new file mode 100644 index 0000000..673a472 --- /dev/null +++ b/android/platform/android/res/layout/picker_entry.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/android/platform/android/res/layout/preview_grid_fragment.xml b/android/platform/android/res/layout/preview_grid_fragment.xml deleted file mode 100644 index 74ab4e9..0000000 --- a/android/platform/android/res/layout/preview_grid_fragment.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/platform/android/res/layout/preview_grid_item.xml b/android/platform/android/res/layout/preview_grid_item.xml deleted file mode 100644 index 32046d0..0000000 --- a/android/platform/android/res/layout/preview_grid_item.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - diff --git a/android/platform/android/res/layout/preview_pager_item_layout.xml b/android/platform/android/res/layout/preview_pager_item_layout.xml deleted file mode 100644 index 4e5d5bf..0000000 --- a/android/platform/android/res/layout/preview_pager_item_layout.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/platform/android/res/layout/print_dialog.xml b/android/platform/android/res/layout/print_dialog.xml index a95840f..1d54d22 100644 --- a/android/platform/android/res/layout/print_dialog.xml +++ b/android/platform/android/res/layout/print_dialog.xml @@ -1,9 +1,9 @@ - + - + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + diff --git a/android/platform/android/res/layout/textentry.xml b/android/platform/android/res/layout/textentry.xml index 0ac4b44..08823df 100644 --- a/android/platform/android/res/layout/textentry.xml +++ b/android/platform/android/res/layout/textentry.xml @@ -1,8 +1,8 @@ - \ No newline at end of file + android:singleLine="false" + android:minLines="3" + android:inputType="textMultiLine" + android:layout_width="match_parent" + android:layout_height="wrap_content" > + diff --git a/android/platform/android/res/values-ar/strings.xml b/android/platform/android/res/values-ar/strings.xml new file mode 100644 index 0000000..f16d5ba --- /dev/null +++ b/android/platform/android/res/values-ar/strings.xml @@ -0,0 +1,54 @@ + + + قبول + MuPDF + إلغاء + تعذر فتح المخزن المؤقت + تعذر فتح المستند + تعذر فتح المستند: %1$s + تعذر فتح الملف: %1$s + اختر قيمة + تم النسخ إلى الحافظة + نسخ + نسخ النص + نسخ النص إلى الحافظة + حذف + تجاهل + يحتوي المستند على تغييرات. هل تريد حفظها؟ + سحب تعليق توضيحي + تعديل التعليقات التوضيحية + أدخل كلمة المرور + دخول إلى وضع إعادة التدفق + تعبئة حقل النص + التنسيق غير مدعوم حاليًا + تظليل + حبر + خروج من وضع إعادة التدفق + المزيد + لا + لم يتم العثور على متكررات أخرى + مشاركة وسائط التخزين مع حاسوب شخصي قد يمنع الوصول إليها + وسائط التخزين غير موجودة + لم يتم تحديد نص + غير مدعوم + لا يوجد شيء لحفظه + موافق + جدول المحتويات + [أعلى مستوى واحد] + %1$s %2$s: %3$s + طباعة + فشلت الطباعة + حفظ + بحث + بحث إلى الخلف + بحث في المستند + بحث إلى الأمام + جاري البحث في&#8230; + تحديد + تحديد النص + شطب + لم يتم العثور على النص + تظليل وتمكين الروابط + تسطير + نعم + diff --git a/android/platform/android/res/values-ca/strings.xml b/android/platform/android/res/values-ca/strings.xml new file mode 100644 index 0000000..ef72886 --- /dev/null +++ b/android/platform/android/res/values-ca/strings.xml @@ -0,0 +1,54 @@ + + + Acceptar + MuPDF + Cancel·lar + No es pot obrir el buffer + No es pot obrir el document + No es pot obrir el document: %1$s + No es pot obrir l\'arxiu: %1$s + Tria el valor + Copiat al portapapers + Copiar + copiar text + Copiar text al portapapers + Esborrar + Descartar + El document té canvis. Desar? + Dibuixar anotació + Editar anotacions + Introduir contrasenya + Entrant en modo de reflux + Emplena el camp de text + Format no suportat actualment + Destacar + Tinta + Abandonant modo de reflux + Més + No + No hi ha més coincidències + Compartir el mitjà d\'emmagatzematge amb un PC pot fer que sigui inaccessible + Mitjà d\'emmagatzematge no present + No s\'ha seleccionat text + No compatible + No hi ha gens que guardar + Acceptar + Índex + [Pujar un nivell] + %1$s %2$s: %3$s + Imprimir + Fallada al imprimir + Desar + Buscar + Buscar cap a enrere + Buscar document + Buscar cap a davant + Buscant… + Seleccionar + Seleccionar text + Ratllat + Text no trobat + Ressaltar i habilitar enllaços + Subratllat + + diff --git a/android/platform/android/res/values-cs/strings.xml b/android/platform/android/res/values-cs/strings.xml new file mode 100644 index 0000000..6c87039 --- /dev/null +++ b/android/platform/android/res/values-cs/strings.xml @@ -0,0 +1,54 @@ + + + Přijmout + MuPDF + Zrušit + Nelze otevřít vyrovnávací paměť + Nelze otevřít dokument + Nelze otevřít dokument: %1$s + Nelze otevřít soubor: %1$s + Zvolte hodnotu + Kopírováno do schránky + Kopírovat + kopírovat text + Kopírovat text do schránky + Smazat + Odmítnout + Dokument byl změněn. Uložit? + Vložit anotaci + Upravit anotace + Zadat heslo + Vstup do režimu přeformátování řádků + Vyplnit textové pole + Formát aktuálně nepodporován + Zvýraznit + Inkoust + Odchod z režimu přeformátování řádků + Více + Ne + Nenalezeny další výskyty + Při sdílení s PC může být paměťové médium nedostupné + Paměťové médim nenalezeno + Nevybrán žádný text + Nepodporováno + Nic k uložení + OK + Obsah + [Nahoru o jednu úroveň] + %1$s %2$s: %3$s + Tisk + Tisk selhal + Uložit + Hledat + Hledat zpět + Prohledat dokument + Hledat vpřed + Hledání&#8230; + Vybrat + Vybrat text + Přeškrtnout + Text nenalezen + Zvýraznit a aktivovat odkazy + Podtrhnout + Ano + diff --git a/android/platform/android/res/values-da/strings.xml b/android/platform/android/res/values-da/strings.xml new file mode 100644 index 0000000..b7de1fd --- /dev/null +++ b/android/platform/android/res/values-da/strings.xml @@ -0,0 +1,54 @@ + + + Accepter + MuPDF + Annuller + Buffer kan ikke åbnes + Dokument kan ikke åbnes + Kan ikke åbne dokumentet: %1$s + Kan ikke åbne filen: %1$s + Vælg værdi + Kopieret til udklipsholder + Kopier + kopier tekst + kopier tekst til udklipsholder + Slet + Afvis + Dokumentet er ændret. Gem ændringer? + Lav anmærkning + Rediger anmærkninger + Indtast adgangskode + Går over til konverteringstilstand + Udfyld tekstfelt + Format ikke understøttet i øjeblikket + Fremhæv + Ink + Forlader konverteringstilstand + Mere + Nej + Der blev ikke fundet flere tilfælde + Deles lagermediet med en PC, kan det gøre det utilgængeligt + Lagermedie ikke fundet + Ingen tekst valgt + Ikke understøttet + Intet at gemme + Okay + Indholdsfortegnelse + [Et niveau op] + %1$s %2$s: %3$s + Udskriv + Udskrivning mislykket + Gem + Søg + Søg bagud + Søg i dokument + Søg fremad + Søger&#8230; + Vælg + Vælg tekst + Gennemstreget + Tekst ikke fundet + Fremhæv og aktiver links + Understreg + Ja + diff --git a/android/platform/android/res/values-de/strings.xml b/android/platform/android/res/values-de/strings.xml new file mode 100644 index 0000000..2e69d36 --- /dev/null +++ b/android/platform/android/res/values-de/strings.xml @@ -0,0 +1,54 @@ + + + Akzeptieren + MuPDF + Abbrechen + Zwischenspeicher kann nicht geöffnet werden + Dokument kann nicht geöffnet werden + Dokument kann nicht geöffnet werden: %1$s + Datei kann nicht geöffnet werden: %1$s + Wert auswählen + In die Zwischenanlage kopiert + Kopieren + Text kopieren + Text in Zwischenablage kopieren + Entfernen + Verwerfen + Das Dokument wurde verändert. Sollen die Änderungen gespeichert werden? + Kommentar einfügen + Kommentar bearbeiten + Passwort eingeben + Anpassungsmodus wird gestartet + Textfeld ausfüllen + Format wird momentan nicht unterstützt + Markieren + Farbe + Anpassungsmodus wird beendet + Mehr + Nein + Keine weiteren Treffer + Die Freigabe des Speichermediums für einen PC kann es unzugänglich machen + Speichermedium nicht vorhanden + Kein Text ausgewählt + Nicht unterstützt + Nichts zum Speichern + OK + Inhaltsverzeichnis + [Eine Ebene nach oben] + %1$s %2$s: %3$s + Drucken + Fehler beim Drucken + Speichern + Suchen + Rückwärts suchen + Dokument durchsuchen + Vorwärts suchen + Suche… + Auswählen + Text auswählen + Durchstreichen + Text konnte nicht gefunden werden + Markiere und aktiviere Verknüpfungen + Unterstreichen + Ja + diff --git a/android/platform/android/res/values-el/strings.xml b/android/platform/android/res/values-el/strings.xml new file mode 100644 index 0000000..f994f28 --- /dev/null +++ b/android/platform/android/res/values-el/strings.xml @@ -0,0 +1,54 @@ + + + Αποδοχή + MuPDF + Ακύρωση + Αδυναμία ανοίγματος buffer + Αδυναμία ανοίγματος εγγράφου + Αδυναμία ανοίγματος εγγράφου: %1$s + Αδυναμία ανοίγματος αρχείου: %1$s + Επιλογή τιμής + Αντιγράφηκε στο πρόχειρο + Αντιγραφή + αντιγραφή κειμένου + Αντιγραφή κειμένου στο πρόχειρο + Διαγραφή + Ματαίωση + Το έγγραφο έχει αλλαγές. Να αποθηκευτούν; + Σχεδίαση σχολίου + Επεξεργασία σχολίων + Πληκτρολογήστε κωδικό πρόσβασης + Είσοδος σε λειτουργία δυναμικής προσαρμογής + Συμπλήρωση πεδίου κειμένου + Αυτή η μορφή δεν υποστηρίζεται τη δεδομένη στιγμή + Επισήμανση + Γραφή + Έξοδος από λειτουργία δυναμικής προσαρμογής + Περισσότερο + Όχι + Δεν βρέθηκαν άλλες εμφανίσεις + Η κοινή χρήση του αποθηκευτικού μέσου με έναν υπολογιστή μπορεί να το καταστήσει μη προσβάσιμο + Δεν υπάρχει αποθηκευτικό μέσο + Δεν έχει επιλεγεί κείμενο + Δεν υποστηρίζεται + Δεν υπάρχει περιεχόμενο για αποθήκευση + ΟΚ + Πίνακας περιεχομένων + [Ένα επίπεδο επάνω] + %1$s %2$s: %3$s + Εκτύπωση + Η εκτύπωση απέτυχε + Αποθήκευση + Αναζήτηση + Αναζήτηση προς τα πίσω + Αναζήτηση εγγράφου + Αναζήτηση προς τα μπροστά + Αναζήτηση&#8230; + Επιλογή + Επιλογή κειμένου + Διακριτή διαγραφή + Δεν βρέθηκε το κείμενο + Επισήμανση και ενεργοποίηση συνδέσεων + Υπογράμμιση + Ναι + diff --git a/android/platform/android/res/values-es/strings.xml b/android/platform/android/res/values-es/strings.xml new file mode 100644 index 0000000..0e28a90 --- /dev/null +++ b/android/platform/android/res/values-es/strings.xml @@ -0,0 +1,54 @@ + + + Aceptar + MuPDF + Cancelar + No se puede abrir el búfer + No se puede abrir el documento + No se puede abrir el documento:%1$s + No se puede abrir el archivo: %1$s + Elegir valor + Copiado al portapapeles + Copiar + copiar texto + Copiar texto al portapapeles + Borrar + Ignorar + El documento tiene cambios. ¿Guardar? + Dibujar anotación + Editar anotaicones + Introducir contraseña + Entrando en el modo de redistribución + Rellenar el campo de texto + Formato actualmente no soportado + Resaltar + Tinta + Saliendo del modo de redistribución + Más + No + No se han encontrado más casos + Compartir el medio de almacenamiento con un PC puede hacerlo inaccesible + Medio de almacenimiento no presente + Texto no seleccionado + No aceptado + Nada que guardar + OK + Tabla de contenidos + [Subir un nivel] + %1$s %2$s: %3$s + Imprimir + No se ha imprimido + Guardar + Buscar + Buscar hacia atrás + Buscar documento + Buscar hacia adelante + Buscando&#8230; + Seleccionar + Seleccionar texto + Tachar + Texto no encontrado + Resaltar y activar + Subrayar + + diff --git a/android/platform/android/res/values-et/strings.xml b/android/platform/android/res/values-et/strings.xml new file mode 100644 index 0000000..fddd25a --- /dev/null +++ b/android/platform/android/res/values-et/strings.xml @@ -0,0 +1,54 @@ + + + Nõustu + MuPDF + Tühista + Ei saa avada puhvrit + Ei saa avada dokumenti + Ei saa avada dokumenti: %1$s + Ei saa avada faili: %1$s + Vali väärtus + Kopeeritud lõikelauale + Kopeeri + kopeeri tekst + Kopeeri tekst lõikelauale + Kustuta + Lõpeta + Dokumendis on tehtud muudatusi. Kas salvestada need? + Tee marginaal + Redigeeri marginaale + Sisesta salasõna + Sisenen ümberpaigutamise režiimi + Täida tekstiväli + Vormingul puudub hetkel tugi + Tõsta esile + Tint + Lahkun ümberpaigutamise režiimist + Veel + Ei + Ei leitud rohkem juhtumeid + Salvestuskandja jagamine arvutiga võib selle juurdepääsmatuks muuta + Salvestuskandja puudub + Teksti ei ole valitud + Puudub tugi + Ei ole midagi salvestada + OK + Sisukord + [Taseme võrra üles] + %1$s%2$s%3$s + Prindi + Printimine ebaõnnestus + Salvesta + Otsi + Otsi tagasisuunas + Otsi dokumendist + Otsi edasisuunas + Otsin&#8230; + Vali + Vali tekst + Läbikriipsutus + Teksti ei leitud + Tõsta lingid esile ja luba need + Jooni alla + Jah + diff --git a/android/platform/android/res/values-fi/strings.xml b/android/platform/android/res/values-fi/strings.xml new file mode 100644 index 0000000..ae13e72 --- /dev/null +++ b/android/platform/android/res/values-fi/strings.xml @@ -0,0 +1,54 @@ + + + Hyväksy + MuPDF + Peruuta + Puskuria ei voi avata + Tiedostoa ei voi avata + Ei voi avata tiedostoa: %1$s + Ei voi avata tiedostoa: %1$s + Valitse arvo + Kopioitu leikepöydälle + Kopioi + kopio teksti + Kopioi teksti leikepöydälle + Poista + Hylkää + Tiedostossa on muutoksia. Haluatko tallentaa muutokset? + Piirrä huomautus + Muokkaa huomautuksia + Anna salasana + Siirrytään takaisinmuuntotilaan + Täytä tekstikenttä + Muotoa ei tällä hetkellä tueta + Korosta + Muste + Poistutaan takaisinmuuntotilasta + Lisää + Ei + Muita esiintymiä ei löydy + Tallennustietovälineen jakaminen tietokoneen kanssa voi estää sen käyttämisen + Tallennustietoväline ei ole käytössä + Ei valittua tekstiä + Ei tuettu + Ei mitään tallennettavaa + OK + Sisällys + [Yksi taso ylöspäin] + %1$s %2$s: %3$s + Tulosta + Tulostus ei onnistunut + Tallenna + Haku + Hae taaksepäin + Hae tiedostosta + Hae eteenpäin + Haetaan &#8230; + Valitse + Valitse teksti + Yliviivaa + Tekstiä ei löydy + Korosta ja ota käyttöön linkit + Alleviivaa + Kyllä + diff --git a/android/platform/android/res/values-fr/strings.xml b/android/platform/android/res/values-fr/strings.xml new file mode 100644 index 0000000..967707b --- /dev/null +++ b/android/platform/android/res/values-fr/strings.xml @@ -0,0 +1,54 @@ + + + Accepter + MuPDF + Annuler + Impossible d\'ouvrir le buffer + Impossible d\'ouvrir le document + Impossible d\'ouvrir le document : %1$s + Impossible d\'ouvrir le fichier : %1$s + Choisir la valeur + Copié dans le presse-papier + Copier + copier le texte + Copier le texte sur le presse-papier + Supprimer + Ignorer + Des modifications ont été effectuées au document. Les sauvegarder ? + Dessiner une note + Éditer une note + Introduire le mot de passe + Entrer en mode refusion + Remplir le champ du texte + Format non compatible pour l\'instant + Surligner + Encre + Quitter le mode refusion + Plus + Non + Aucune occurrence trouvée + Sauvegarder le support de stockage avec un PC peut le rendre inaccessible + Support de stockage absent + Aucun texte sélectionné + Non compatible + Rien à sauvegarder + OK + Table des matières + [Niveau supérieur] + %1$s%2$s : %3$s + Imprimer + L\'impression a échoué + Sauvegarder + Rechercher + Rechercher en arrière + Rechercher document + Rechercher en avant + Chercher&#8230 ; + Sélectionner + Sélectionner le texte + Rayer + Texte introuvable + Surligner et autoriser les liens + Souligner + Oui + diff --git a/android/platform/android/res/values-hi/strings.xml b/android/platform/android/res/values-hi/strings.xml new file mode 100644 index 0000000..4d09a97 --- /dev/null +++ b/android/platform/android/res/values-hi/strings.xml @@ -0,0 +1,54 @@ + + + स्वीकार करें + MuPDF + रद्द करें + बफ़र खोल नहीं सके + दस्तावेज़ खोल नहीं सके + दस्तावेज़ नहीं खोल सके: %1$s + फ़ाइल खोल नहीं सके: %1$s + मान चुनें + क्लिपबोर्ड में कॉप कर दिया गया + कॉपी करें + पाठ कॉपी करें + पाठ को क्लिपबोर्ड में कॉपी करें + हटाएँ + खारिज करें + दस्तावेज़ में परिवर्तन हैं। उन्हें सहेजें? + एनोटेशन बनाएँ + एनोटेशनों को संपादित करें + पासवर्ड दर्ज करें + रीफ़्लो मोड में प्रवेश कर रहे हैं + पाठ फ़ील्ड को भरें + इस समय इस फ़ॉर्मेट को समर्थन नहीं प्राप्त है + हाइलाइट करें + स्याही + रीफ़्लो मोड को छोड़ रहे हैं + और भी + नहीं + यह और कहीं नहीं मिला + संग्रह माध्यम को पीसी के साथ साझा करने से उस तक पहुँचना मुश्किल हो सकता है + संग्रह माध्यम मौजूद नहीं है + कोई भी पाठ नहीं चुना गया है + असमर्थित + सहेजने के लिए कुछ नहीं है + ठीक है + विषय सूची + [एक स्तर ऊपर] + %1$s%2$s:%3$s + मुद्रित करें + मुद्रण विफल हुआ + सहेजें + खोजें + पीछे की ओर खोजें + दस्तावेज़ में खोजें + आगे की ओर खोजें + &#8230 को खोज रहे हैं; + चुनें + पाठ चुनें + काटें + पाठ नहीं मिला + लिंकों को हाइलाइट और सक्षम करें + रेखांकित करें + हाँ + diff --git a/android/platform/android/res/values-hu/strings.xml b/android/platform/android/res/values-hu/strings.xml new file mode 100644 index 0000000..1533b65 --- /dev/null +++ b/android/platform/android/res/values-hu/strings.xml @@ -0,0 +1,54 @@ + + + Elfogadás + MuPDF + Mégse + A puffert nem lehet megnyitni + A dokumentumot nem lehet megnyitni + A dokumentumot nem lehet megnyitni: %1$s + A fájlt nem lehet megnyitni: %1$s + Érték kiválasztása + A vágólapra másolva + Másolás + szöveg másolása + Szöveg másolása a vágólapra + Törlés + Bezárás + A dokumentum módosítva lett. Menti a változtatásokat? + Jegyzet rajzolása + Jegyzetek szerkesztése + Jelszó megadása + Belépés az újrarendezési módba + Szövegmező kitöltése + A formátum jelenleg nem támogatott + Kiemelés + Kézírás + Kilépés az újrarendezési módból + Több + Nem + Nincsenek további találatok + Az adathordozó a PC-vel való megosztás esetén elérhetetlenné válhat + Nincs jelen adathordozó + Nincs kijelölt szöveg + Nem támogatott + Nem kell semmit menteni + OK + Tartalomjegyzék + [Egy szinttel feljebb] + %1$s %2$s: %3$s + Nyomtatás + Nyomtatás sikertelen + Mentés + Keresés + Keresés visszafelé + Dokumentum keresése + Keresés előrefelé + Keresés:&#8230; + Kijelölés + Szöveg kijelölése + Áthúzás + Szöveg nem található + Kiemelés és linkek engedélyezése + Aláhúzás + Igen + diff --git a/android/platform/android/res/values-in/strings.xml b/android/platform/android/res/values-in/strings.xml new file mode 100644 index 0000000..f90d1b3 --- /dev/null +++ b/android/platform/android/res/values-in/strings.xml @@ -0,0 +1,54 @@ + + + Terima + MuPDF + Batal + Tidak bisa membuka penyangga + Tidak bisa membuka dokumen + Tidak bisa membuka dokumen: %1$s + Tidak bisa membuka berkas: %1$s + Pilih nilai + Disalin ke papan klip + Salin + Salin teks + Salin teks ke papan klip + Hapus + Hilangkan + Dokumen telah berubah. Simpan perubahan? + Gambar anotasi + Sunting anotasi + Masukkan kata sandi + Masuk mode alir-ulang + Isi bidang teks + Format ini tidak didukung + Sorotan + Tinta + Tinggalkan mode alir-ulang + Selengkapnya + Tidak + Tidak ditemukan kejadian lain + Berbagi media penyimpanan dengan PC dapat membuatnya tidak bisa diakses + Media penyimpanan tidak ada + Tidak ada teks yang dipilih + Tidak didukung + Tidak ada yang disimpan + Oke + Daftar Isi + [Naik satu tingkat] + %1$s %2$s: %3$s + Cetak + Pencetakan gagal + Simpan + Cari + Cari mundur + Cari dokumen + Cari maju + Mencari… + Pilih + Pilih teks + Gagal + Teks tidak ditemukan + Sorot dan aktifkan tautan + Garis bawah + Ya + diff --git a/android/platform/android/res/values-it/strings.xml b/android/platform/android/res/values-it/strings.xml new file mode 100644 index 0000000..25cf56d --- /dev/null +++ b/android/platform/android/res/values-it/strings.xml @@ -0,0 +1,54 @@ + + + Accetta + MuPDF + Annulla + Impossibile aprire buffering + Impossibile aprire documento + Impossibile aprire documento: %1$s + Impossibile aprire file: %1$s + Scegli valore + Copiato negli appunti + Copia + copia testo + Copia testo negli appunti + Elimina + Ignora + Il documento contiene modifiche. Salvare? + Disegna annotazione + Modifica annotazione + Inserisci password + Inserimento modalità di adattamento dinamico del contenuto + Riempi il campo di testo + Formato attualmente non supportato + Evidenzia + Inchiostro + Abbandono della modalità di adattamento dinamico del contenuto + Altro + No + Nessun\'altra occorrenza trovata + La condivisione del supporto di archiviazione con un PC può renderlo inaccessibile + Supporto di archiviazione non presente + Nessun testo selezionato + Non supportato + Niente da salvare + Ok + Sommario + [Su di un livello] + %1$s %2$s: %3$s + Stampa + Stampa non riuscita + Salva + Cerca + Cerca indietro + Cerca documento + Cerca avanti + Ricerca... + Seleziona + Seleziona testo + Barrato + Testo non trovato + Evidenzia e abilita link + Sottolinea + + diff --git a/android/platform/android/res/values-iw/strings.xml b/android/platform/android/res/values-iw/strings.xml new file mode 100644 index 0000000..d259ae7 --- /dev/null +++ b/android/platform/android/res/values-iw/strings.xml @@ -0,0 +1,54 @@ + + + קבל + MuPDF + בטל + אין אפשרות לפתוח מאגר + אין אפשרות לפתוח מסמך + אין אפשרות לפתוח מסמך: %1$s + אין אפשרות לפתוח קובץ: %1$s + בחר ערך + הועתק ללוח + העתק + העתק טקסט + העתק טקסט ללוח + מחק + התעלם + קיימים שינויים במסמך. לשמור אותם? + רשום ביאור + ערוך ביאורים + הזן סיסמה + כניסה למצב הזרמה מחדש + מלא את שדה הטקסט + תבנית לא נתמכת כעת + הבלטה + דיו + יציאה ממצב הזרמה מחדש + עוד + לא + לא עוד + שיתוף מדיית האחסון עם מחשב עשויה להפוך אותה לבלתי נגישה + מדיית אחסון לא קיימת + לא נבחר טקסט + לא נתמך + אין מה לשמור + בסדר + תוכן העניינים + [למעלה ברמה אחת] + %1$s %2$s: %3$s + הדפס + ההדפסה נכשלה + שמור + חפש + חפש אחורה + חפש במסמך + חפש קדימה + מחפש&#8230; + בחר ערך + בחר טקסט + הדגש + לא נמצא טקסט + הבלט ואפשר קישורים + קו תחתון + כן + diff --git a/android/platform/android/res/values-ja/strings.xml b/android/platform/android/res/values-ja/strings.xml new file mode 100644 index 0000000..8ceb5e0 --- /dev/null +++ b/android/platform/android/res/values-ja/strings.xml @@ -0,0 +1,54 @@ + + + 承諾する + MuPDF + キャンセル + バッファーを開けません + ドキュメントを開けません + 次のドキュメントを開けません:%1$s + 次のファイルを開けません: %1$s + バリューを選択してください + クリップボードにコピーされました + コピー + テキストをコピー + テキストをクリップボードにコピー + 削除 + 却下 + ドキュメントは変更されました。保存しますか? + 注釈を挿入する + 注釈を編集する + パスワードを入力する + リフローモードを開始する + テキストフィールドに書き込む + このフォーマットは現在サポートされていません + ハイライト + インク + リフローモードを終了する + もっと + いいえ + 他にオカレンスは見つかりませんでした + 記憶媒体をPCとシェアするとアクセスできなくなる可能性があります + 記憶媒体が見つかりません + テキストが選択されていません + サポートされていません + 保存するものがありません + 了解 + 目次 + [一つ上位のレベル] + %1$s %2$s: %3$s + 印刷 + 印刷に失敗しました + 保存 + 検索 + 逆方向検索 + ドキュメントを検索する + 順方向検索 + 検索中 + 選択 + テキストを選択する + 取り消し線を引く + テキストが見つかりません + ハイライトしてリンクを有効にする + 下線を引く + はい + diff --git a/android/platform/android/res/values-ko/strings.xml b/android/platform/android/res/values-ko/strings.xml new file mode 100644 index 0000000..b52a2f5 --- /dev/null +++ b/android/platform/android/res/values-ko/strings.xml @@ -0,0 +1,54 @@ + + + 수락 + MuPDF + 취소 + 버퍼 열 수 없음 + 문서 열 수 없음 + 문서 열 수 없음: %1$s + 파일 열 수 없음: %1$s + 값 선택 + 클립보드로 복사됨 + 복사 + 텍스트 복사 + 클립보드로 텍스트 복사 + 삭제 + 무시 + 문서에 변경사항이 있습니다. 저장? + 주석달기 + 주석 편집 + 패스워드 입력 + 리플로우 모드 시작 + 텍스트 입력란에 기입하십시오. + 현재 지원되지 않는 포맷 + 주요기능 + 잉크 + 리플로우 모드 해제 + 기타 + 아니오 + 발견된 추가 발생 없음 + PC와 스토리지 미디어를 공유하면 액세스할 수 없습니다. + 스토리지 미디어 없음 + 선택된 텍스트 없음 + 지원 안됨 + 저장 대상 없음 + 확인 + 목차 + [레벨 한 단계 상승] + %1$s %2$s: %3$s + 인쇄 + 인쇄 실패 + 저장 + 검색 + 뒤로 검색 + 문서 검색 + 앞으로 검색 + 검색 중&#8230; + 선택 + 텍스트 선택 + 삭제 + 발견된 텍스트 없음 + 하이라이트 및 링크 활성화 + 밑줄 + + diff --git a/android/platform/android/res/values-lt/strings.xml b/android/platform/android/res/values-lt/strings.xml new file mode 100644 index 0000000..f66ba30 --- /dev/null +++ b/android/platform/android/res/values-lt/strings.xml @@ -0,0 +1,54 @@ + + + Priimti + „MuPDF“ + Atšaukti + Nepavyksta atverti buferinės atmintinės + Nepavyksta atverti dokumento + Nepavyksta atverti dokumento: %1$s + Nepavyksta atverti failo: %1$s + Pasirinkti vertę + Nukopijuota į iškarpinę + Kopijuoti + kopijuoti tekstą + Kopijuoti tekstą į iškarpinę + Naikinti + Atmesti + Dokumente yra pakeitimų. Ar juos įrašyti? + Braižyti anotaciją + Redaguoti anotacijas + Įvesti slaptažodį + Pereinama į pertvarkymo režimą + Užpildyti teksto lauką + Formatas šiuo metu nedera + Pažymėti + Rašalas + Išeinama iš pertvarkymo režimo + Daugiau + Ne + Daugiau įrašų nerasta + Pabendrinus laikmeną su kompiuteriu, ji gali tapti nebepasiekiama + Laikmenos nėra + Neparinktas tekstas + Nedera + Nėra ką įrašyti + Gerai + Turinys + [Vienu lygiu aukštyn] + %1$s %2$s: %3$s + Spausdinti + Išspausdinti nepavyko + Įrašyti + Ieškoti + Ieškoti atgal + Ieškoti dokumente + Ieškoti pirmyn + Ieškoma&#8230; + Pasirinkti + Pasirinkti tekstą + Išbraukti + Teksto nerasta + Pažymėti ir įjungti nuorodas + Pabraukti + Taip + diff --git a/android/platform/android/res/values-lv/strings.xml b/android/platform/android/res/values-lv/strings.xml deleted file mode 100644 index 64af4e0..0000000 --- a/android/platform/android/res/values-lv/strings.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - Failu glabātuve nav pieejama - Savienojot failu glabātuvi ar datoru tā paliek neizmantojama - Atcelt - Meklēt iepriekšejo - Meklēt nākamo - Meklēt dokumentā - %1$s %2$s: %3$s - Satura rādītājs - Ievadiet paroli - Teksts nav atrasts - Meklēšana… - Izcelt un aktivizēt linkus - Nav atrasti citi notikumi - Iezīmēt - Meklēt - Kopēt - Pārsvītrots - Dzēst - Izcelts - Apakšstrīpa - Labot anotacijas - Tinte - Saglabāt - Drukāt - Atcelt - [Vienu līmeni augstāk] - - - Ieiet reflow režīmā - Iziet no reflow režīma - Drukāšana neveiksmīga - Iezīmēt tekstu - Iekopēts buferī - Teksts nav iezīmēts - Zīmēt anotāciju - Nav nekas saglabājams - Dokumentā ir izmaiņas. Saglabāt tās? - Neizdevās atvērt dokumentu - Neizdevās atvērt dokumentu: %1$s - Neizdevās atvērt failu: %1$s - Neizdevās atvērt buffei - Aizpildīt teksta lauku - Labi - Izvēlēties vērtību - Netiek atbalstīts - Iekopēt tekstu buferī - Vairāk - Piekrītu - kopēt tekstu - Šīs formāts patreiz netiek atbalstīts - Pārslēgties reflow režīnā - - %1$d no %2$d - %1$d-%2$d no %3$d - - diff --git a/android/platform/android/res/values-ms/strings.xml b/android/platform/android/res/values-ms/strings.xml new file mode 100644 index 0000000..64541e6 --- /dev/null +++ b/android/platform/android/res/values-ms/strings.xml @@ -0,0 +1,54 @@ + + + Terima + MuPDF + Batal + Tidak boleh membuka penimbal + Tidak boleh membuka dokumen + Tidak boleh membuka dokumen: %1$s + Tidak boleh membuka fail: %1$s + Pilih nilai + Disalin ke papan klip + Salin + salin teks + Salin teks ke papan klip + Padam + Singkir + Dokumen mempunyai perubahan. Simpankannya? + Lakarkan catatan + Suntingkan catatan + Masukkan kata laluan + Memasuki mod penyusunan semula + Mengisi medan teks + Format buat masa ini tidak disokong + Serlahkan + Dakwat + Meninggalkan mod penyusunan semula + Lagi + Tidak + Tiada kejadian lanjut ditemui + Berkongsi media storan dengan PC boleh menjadikannya tidak dapat dicapai + Media storan tidak wujud + Tiada teks dipilih + Tidak disokong + Tiada apa untuk disimpan + Okey + Jadual Kandungan + [Naik satu tahap] + %1$s %2$s: %3$s + Cetak + Gagal dicetak + Simpan + Carian + Carian ke belakang + Carian dokumen + Carian ke depan + Mencari&#8230; + Pilih + Pilih teks + Mansuhkan + Teks tidak ditemui + Serlahkan dan dayakan pautan + Gariskan + Ya + diff --git a/android/platform/android/res/values-nl/strings.xml b/android/platform/android/res/values-nl/strings.xml new file mode 100644 index 0000000..21945c8 --- /dev/null +++ b/android/platform/android/res/values-nl/strings.xml @@ -0,0 +1,54 @@ + + + Accepteren + MuPDF + Annuleren + Buffer kan niet geopend worden + Document kan niet geopend worden + Document kan niet geopend worden: %1$s + Bestand kan niet geopend worden : %1$s + Kies waarde + Gekopieerd naar klembord + Kopiëren + tekst kopiëren + Tekst kopiëren naar klembord + Verwijderen + Afwijzen + Het document is gewijzigd. Opslaan? + Opmerking tekenen + Opmerkingen bewerken + Voer wachtwoord in + Conversiemodus wordt geopend + Vul het tekstveld in + Formaat wordt momenteel niet ondersteund + Markeren + Inkten + Conversiemodus wordt beëindigd + Meer + Nee + Geen andere resultaten gevonden + Het opslagmedium kan ontoegankelijk worden als het met een pc wordt gedeeld + Geen opslagmedium aanwezig + Geen tekst geselecteerd + Niet ondersteund + Niets om op te slaan + Oké + Inhoudsopgave + [Een niveau hoger] + %1$s %2$s: %3$s + Afdrukken + Afdrukken mislukt + Opslaan + Zoeken + Achterstevoren zoeken + Document doorzoeken + Vooruit zoeken + Aan het zoeken … + Selecteren + Tekst selecteren + Doorhalen + Tekst niet gevonden + Markeren en koppelingen inschakelen + Onderstrepen + Ja + diff --git a/android/platform/android/res/values-no/strings.xml b/android/platform/android/res/values-no/strings.xml new file mode 100644 index 0000000..31bd6bf --- /dev/null +++ b/android/platform/android/res/values-no/strings.xml @@ -0,0 +1,54 @@ + + + Aksepter + MuPDF + Avbryt + Kan ikke åpne buffer + Kan ikke åpne dukumentet + Kan ikke åpne dokumentet: %1$s + Kan ikke åpne filen: %1$s + Velg verdi + Kopiert til utklippstavlen + Kopier + kopier tekst + Kopier teksten til utklippstavlen + Slett + Avvis + Det er endringer i dokumentet. Lagre dem? + Lag merknad + Rediger merknader + Skriv inn passord + Bytter til konverteringsmodus + Fyll ut tekstfeltet + Formatet er ikke støttet for øyeblikket + Uthev + Håndskrift + Går ut av konverteringsmodus + Mer + Nei + Ingen flere hendelser funnet + Deling av lagringsmedia med en PC kan gjøre det utilgjengelig + Lagringsmedia ikke til stede + Ingen tekst er valgt + Ikke støttet + Ingenting å lagre + Ok + Innholdsfortegnelse + [OPP ett nivå] + %1$s%2$s%3$s + Skriv ut + Kunne ikke skrive ut + Lagre + Søk + Søk bakover + Søk i dokument + Søk framover + Søker&#8230; + Velg + Valgt tekst + Gjennomstreking + Teksten ble ikke funnet + Uthev og aktiver koblinger + Understrek + Ja + diff --git a/android/platform/android/res/values-pl/strings.xml b/android/platform/android/res/values-pl/strings.xml new file mode 100644 index 0000000..42511e4 --- /dev/null +++ b/android/platform/android/res/values-pl/strings.xml @@ -0,0 +1,54 @@ + + + Zaakceptuj + MuPDF + Anuluj + Nie można otworzyć bufora + Nie można otworzyć dokumentu + Nie można otworzyć dokumentu: %1$s + Nie można otworzyć pliku: %1$s + Wybierz wartość + Skopiowano do schowka + Kopiuj + kopiuj tekst + Kopiuj tekst do schowka + Usuń + Odrzuć + W dokumencie dokonano zmian. Czy chcesz je zapisać? + Sporządź notatkę + Edytuj notatki + Wprowadź hasło + Włączanie trybu zawijania tekstu + Wypełnij pole tekstowe + Format obecnie nieobsługiwany + Podświetl + Atrament + Wyłączanie trybu zawijania tekstu + Więcej + Nie + Nie znaleziono więcej wystąpień + Współdzielenie nośnika danych z komputerem PC może sprawić, że będzie niedostępny + Nośnik danych niedostępny + Nie wybrano tekstu + Nieobsługiwany + Nie ma nic do zapisania + OK + Spis treści + [W górę o jeden poziom] + %1$s %2$s: %3$s + Drukuj + Drukowanie nieudane + Zapisz + Szukaj + Szukaj z tyłu + Szukaj w dokumencie + Szukaj z przodu + Wyszukiwanie&#8230; + Wybierz + Wybierz tekst + Przekreślenie + Nie znaleziono tekstu + Podświetl i aktywuj linki + Podkreślenie + Tak + diff --git a/android/platform/android/res/values-pt/strings.xml b/android/platform/android/res/values-pt/strings.xml new file mode 100644 index 0000000..15f8628 --- /dev/null +++ b/android/platform/android/res/values-pt/strings.xml @@ -0,0 +1,54 @@ + + + Aceitar + MuPDF + Cancelar + Não é possível abrir a memória intermédia + Não é possível abrir o documento + Não é possível abrir o documento: %1$s + Não é possível abrir o ficheiro: %1$s + Escolha um valor + Copiado para a área de transferência + Copiar + copiar o texto + Copiar o texto para a área de transferência + Eliminar + Desistir + Há alterações ao documento. Deseja guardá-las? + Adicionar anotação + Editar anotações + Escrever a palavra-passe + A entrar no modo de refluxo + Preencher o campo de texto + Esse formato não é atualmente suportado + Destacar + Tinta + A sair do modo de refluxo + Mais + Não + Não foram encontradas mais ocorrências + Partilhar o dispositivo de armazenamento com um PC poderá torná-lo inacessível + O dispositivo de armazenamento não está presente + Não há texto selecionado + Não suportado + Não há nada para guardar + Okay + Índice + [subir um nível] + %1$s%2$s: %3$s + Imprimir + Falha na Impressão + Guardar + Pesquisar + Pesquisar para trás + Pesquisar no documento + Pesquisar para a frente + A pesquisar&#8230; + Selecionar + Selecionar o texto + Rasurado + Texto não encontrado + Destacar e permitir links + Sublinhado + Sim + diff --git a/android/platform/android/res/values-ru/strings.xml b/android/platform/android/res/values-ru/strings.xml new file mode 100644 index 0000000..7cc3518 --- /dev/null +++ b/android/platform/android/res/values-ru/strings.xml @@ -0,0 +1,54 @@ + + + Принять + MuPDF + Отмена + Невозможно открыть буфер + Невозможно открыть документ + Невозможно открыть документ: %1$s + Невозможно открыть файл: %1$s + Выберите значение + Скопировано в буфер + Копировать + копировать текст + Копировать текст в буфер + Удалить + Пропустить + Документ был изменен. Сохранить изменения? + Создать аннтоацию + Редактировать аннотации + Введите пароль + Переход в режим Reflow + Заполните текстовое поле + Формат не поддерживается + Выделить + Чернила + Выход из режима Reflow + Еще + Нет + Других ошибок не зафиксировано + Подключение компьютеров к хранилищу данных может привести к потере доступа к хранилищу + Хранилище данных отсутствует + Текст не выбран + Не поддерживается + Не выбраны файлы для сохранения + ОК + Содержание + [Вверх на один уровень] + %1$s %2$s: %3$s + Печать + Печать не выполнена + Сохранить + Поиск + Искать в предыдущей части документа + Искать в документе + Искать в остальной части документа + Поиск&#8230; + Выбор + Выбрать текст + Зачеркнуть + Текст не найден + Выделить и включить ссылки + Подчеркнуть + Да + diff --git a/android/platform/android/res/values-sk/strings.xml b/android/platform/android/res/values-sk/strings.xml new file mode 100644 index 0000000..e11737e --- /dev/null +++ b/android/platform/android/res/values-sk/strings.xml @@ -0,0 +1,54 @@ + + + Prijať + MuPDF + Zrušiť + Buffer sa nedá otvoriť + Dokument sa nedá otvoriť + Nedá sa otvoriť dokument: %1$s + Nedá sa otvoriť súbor: %1$s + Vyberte si hodnotu + Skopírované do vyrovnávacej pamäti + Kopírovať + kopírovať text + Kopírovať text do vyrovnávacej pamäti + Zmazať + Zrušiť + Dokument bol zmený. Uložiť zmeny? + Zostaviť anotáciu + Upraviť anotácie + Zadať heslo + Vstupujem do režimu opätovného nalievania + Vyplniť textové pole + Tento formát momentálne nepodporujem + Zvýrazniť + Atrament + Vystupujem z režimu opätovného nalievania + Viac + Nie + Viac príkladov sa nenašlo + Zdieľanie úložného média s PC môže znemožniť prístup + Nie je tu úložné médium + Žiadny text nie je vybraný + Nepodporované + Niet čo uložiť + Dobre + Obsah + [O úroveň vyššie] + %1$s %2$s: %3$s + Tlačiť + Tlačenie zlyhalo + Uložiť + Hľadať + Hľadať spätne + Hľadať v dokumente + Hľadať dopredu + Hľadám&#8230; + Vybrať + Vybrať text + Preškrtnúť + Text sa nenašiel + Zvýrazniť a zapnúť linky + Podčiarknúť + Áno + diff --git a/android/platform/android/res/values-sv/strings.xml b/android/platform/android/res/values-sv/strings.xml new file mode 100644 index 0000000..61d14d0 --- /dev/null +++ b/android/platform/android/res/values-sv/strings.xml @@ -0,0 +1,54 @@ + + + Acceptera + MuPDF + Avbryt + Kan inte öppna buffer + Kan inte öppna dokument + Kan inte öppna dokument: %1$s + Kan inte öppna fil: %1$s + Välj värde + Kopierat till klippbordet + Kopiera + kopiera text + Kopiera text till klippbordet + Ta bort + Avfärda + Dokumentet har ändrats. Spara ändringar? + Rita annotation + Ändra annotation + Fyll i lösenord + Aktiverar reflow-läge + Fyll i textfält + Formatat stöds inte för närvarande + Markera + Bläck + Lämnar reflow-läge + Mer + Nej + Inga flera förekomster hittades + Att dela lagringsmediet med en PC kan göra den oåtkomlig + Lagringsmedia finns inte + Ingen text har valts + Stöds ej + Inget att spara + OK + Innehållsförteckning + [Upp en nivå] + %1$s %2$s: %3$s + Skriv ut + Utskrift misslyckades + Spara + Sök + Sök bakåt + Sök dokument + Sök framåt + Letar&#8230; + Välj + Välj text + Stryk + Text hittades ej + Markera och aktivera länkar + Understryk + Ja + diff --git a/android/platform/android/res/values-th/strings.xml b/android/platform/android/res/values-th/strings.xml new file mode 100644 index 0000000..e682712 --- /dev/null +++ b/android/platform/android/res/values-th/strings.xml @@ -0,0 +1,54 @@ + + + ยอมรับ + MuPDF + ยกเลิก + ไม่สามารถเปิดบัฟเฟอร์ + ไม่สามารถเปิดเอกสาร + ไม่สามารถเปิดเอกสาร: %1$s + ไม่สามารถเปิดไฟล์: %1$s + เลือกค่า + คัดลอกไปที่คลิปบอร์ดแล้ว + คัดลอก + คัดลอกข้อความ + คัดลอกข้อความไปที่คลิปบอร์ด + ลบ + เลิกใช้ + เอกสารมีการเปลี่ยนแปลง ต้องการบันทึกหรือไม่ + เขียนคำอธิบายประกอบ + แก้ไขคำอธิบายประกอบ + ป้อนรหัสผ่าน + เข้าสู่โหมดเรียงหน้ากระดาษใหม่ + เติมในช่องข้อความ + ไม่รองรับรูปแบบในขณะนี้ + ไฮไลท์ + หมึก + ออกจากโหมดเรียงหน้ากระดาษใหม่ + เพิ่มเติม + ไม่ + ไม่พบเหตุการณ์ที่เกิดขึ้นเพิ่มเติม + การแบ่งปันสื่อจัดเก็บข้อมูลกับพีซีสามารถทำให้สื่อจัดเก็บข้อมูลไม่สามารถเข้าถึงได้ + สื่อเก็บข้อมูลไม่ปรากฏ + ไม่มีข้อความที่เลือก + ไม่รองรับ + ไม่มีอะไรให้บันทึก + ตกลง + สารบัญ + [ขึ้นหนึ่งระดับ] + %1$s %2$s: %3$s + พิมพ์ + พิมพ์ล้มเหลว + บันทึก + ค้นหา + ค้นหาย้อนกลับ + ค้นหาเอกสาร + ค้นหาไปข้างหน้า + กำลังค้นหา&#8230; + เลือก + เลือกข้อความ + ขีดทับ + ไม่พบข้อความ + ไฮไลท์และเปิดใช้งานลิงก์ + ขีดเส้นใต้ + ใช่ + diff --git a/android/platform/android/res/values-tl/strings.xml b/android/platform/android/res/values-tl/strings.xml new file mode 100644 index 0000000..39611fc --- /dev/null +++ b/android/platform/android/res/values-tl/strings.xml @@ -0,0 +1,54 @@ + + + Tanggapin + MuPDF + Kanselahin + Hindi mabuksan ang buffer + Hindi mabuksan ang dokumento + Hindi mabuksan ang dokumentong: %1$s + Hindi mabuksan ang file na: %1$s + Pumili ng halaga + Kinopya sa clipboard + Kopyahin + kopyahin ang teksto + Kopyahin ang teksto sa clipboard + Alisin + Umalis + May mga pagbabago sa dokumento. I-save ang mga ito? + Gumuhit ng anotasyon + I-edit ang mga anotasyon + Ilagay ang password + Pumapasok sa reflow mode + Punan ang puwang para sa teksto + Ang format ay kasalukuyang hindi gumagana dito + I-highlight + Lagdaan (Ink) + Umaalis sa reflow mode + Higit pa + Hindi + Walang nahanap na karagdagang paglitaw + Ang pagbabahagi ng storage media sa isang PC ay gagawin itong hindi magagamit + Walang storage media + Walang piniling teksto + Hindi gumagana dito + Walang ise-save + Okay + Talaan ng Nilalaman + [Umakyat ng isang antas] + %1$s %2$s: %3$s + I-print + Hindi nai-print + I-save + Maghanap + Maghanap pabalik + Maghanap sa dokumento + Maghanap nang pasulong + Hinahanap ang&#8230; + Piliin + Piliin ang teksto + Guhitan ang teksto (strike-out) + Hindi nahanap ang teksto + I-highlight at paganahin ang mga link + Guhitan + Oo + diff --git a/android/platform/android/res/values-tr/strings.xml b/android/platform/android/res/values-tr/strings.xml new file mode 100644 index 0000000..c64ab7c --- /dev/null +++ b/android/platform/android/res/values-tr/strings.xml @@ -0,0 +1,54 @@ + + + Kabul et + MuPDF + İptal et + Arabellek açılamıyor + Belge açılamıyor + Belge açılamıyor: %1$s + Dosya açılamıyor: %1$s + Değeri seç + Panoya kopyalandı + Kopyala + metni kopyala + Metni panoya kopyala + Sil + Bırak + Belgede değişiklikler var. Kaydetmek istiyor musunuz? + Ek açıklama çiz + Ek açıklamalar düzenle + Şifreyi gir + Yeniden akma moduna giriyor + Metin alanını doldurun + Bu format şu an için desteklenmiyor + Vurgula + Mürekkep + Yeniden akma modundan çıkılıyor + Daha fazla + Hayır + Daha fazla öğe bulunamadı + Depolama ortamının bilgisayar ile paylaşımı onu erişilmez yapabilir + Depolama ortamı bulunmuyor + Seçili metin bulunmuyor + Desteklenmiyor + Kaydedecek bir şey yok + Tamam + İçindekiler Tablosu + [Bir seviye üste çık] + %1$s %2$s: %3$s + Yazdır + Yazdırma başarısız oldu + Kaydet + Ara + Geriye doğru ara + Belge ara + İleriye doğru ara + Aranıyor&#8230; + Seç + Metin seç + Üstünü çiz + Metin bulunamadı + Bağlantıları vurgula ve etkinleştir + Altını çiz + Evet + diff --git a/android/platform/android/res/values-zh-rTW/strings.xml b/android/platform/android/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000..4cd8970 --- /dev/null +++ b/android/platform/android/res/values-zh-rTW/strings.xml @@ -0,0 +1,54 @@ + + + 同意 + MuPDF + 取消 + 未能開啟緩衝 + 未能開啟文件 + 未能開啟文件: %1$s + 未能開啟檔案%1$s + 選擇數值 + 複製至剪貼簿 + 複製 + 複製文字 + 複製文字至剪貼簿 + 刪除 + 取消 + 你需要儲存已編輯的文件嗎? + 繪畫註釋 + 編輯註釋 + 輸入密碼 + 根據螢幕大小顯示 + 填寫文字欄 + 暫時不支援此格式 + 標示重點 + 墨水 + 不根據螢幕大小顯示 + 更多 + 沒有 + 沒有相符項目 + 未能與電腦分享存放裝置 + 沒有存放裝置 + 沒有選擇文字 + 不支援 + 沒有資料儲存 + 完成 + 目錄 + [升一級] + %1$s%2$s%3$s + 列印 + 列印失敗 + 儲存 + 搜尋 + 往後搜尋 + 搜尋文件 + 往前搜尋 + 搜尋中&#8230; + 選擇 + 選擇文字 + 刪除線 + 未能找到文字 + 標示及允許連結 + 在下面劃線 + + diff --git a/android/platform/android/res/values-zh/strings.xml b/android/platform/android/res/values-zh/strings.xml new file mode 100644 index 0000000..60fcbb8 --- /dev/null +++ b/android/platform/android/res/values-zh/strings.xml @@ -0,0 +1,54 @@ + + + 接受 + MuPDF + 取消 + 无法打开缓冲器 + 无法打开文档 + 无法打开文档: %1$s + 无法打开文件:%1$s + 选择值 + 已复制到剪贴板 + 复制 + 复制文本 + 将文本复制到剪贴板 + 删除 + 解除 + 文档已变更,保存变更吗? + 作批注 + 编辑批注 + 输入密码 + 输入重排模式 + 填充文本字段 + 当前不支持此格式 + 高亮 + 墨迹 + 正在离开重排模式 + 更多 + + 未发现更多实例。 + 存储介质在设备和 PC 上共同使用,会导致该存储介质在设备上无法被访问 + 没有存储介质 + 未选择文本 + 不被支持 + 没有要保存的内容 + 确定 + 目录 + [向上一级] + %1$s%2$s:%3$s + 打印 + 未能打印 + 保存 + 搜索 + 向后搜索 + 搜索文档 + 向前搜索 + 正在搜索… + 选择 + 选择文本 + 删除线 + 未发现文本 + 高亮并启用墨迹 + 下划线 + + diff --git a/android/platform/android/res/values/colors.xml b/android/platform/android/res/values/colors.xml index 34586c4..ecd1519 100644 --- a/android/platform/android/res/values/colors.xml +++ b/android/platform/android/res/values/colors.xml @@ -1,23 +1,16 @@ - #FF404040 - #C0202020 - #EEEEEEEE - #C0000000 - #C0202020 - #C0202020 - #00000000 - #FF2572AC - #FFFFFF - #FFFFFF - #000000 - #2572AC - #000000 - #2572AC - #FFFFFF - - #FF33B5E5 - - #FFFFFF - #BBA8A8A8 + #404040 + #C0000000 + #C0202020 + #C0202020 + #00000000 + #FF2572AC + #FFFFFF + #FFFFFF + #000000 + #2572AC + #000000 + #2572AC + #FFFFFF diff --git a/android/platform/android/res/values/dimens.xml b/android/platform/android/res/values/dimens.xml deleted file mode 100644 index 00f85ec..0000000 --- a/android/platform/android/res/values/dimens.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - 60dip - - 180dip - - \ No newline at end of file diff --git a/android/platform/android/res/values/strings.xml b/android/platform/android/res/values/strings.xml index 0a031a1..7949dc7 100644 --- a/android/platform/android/res/values/strings.xml +++ b/android/platform/android/res/values/strings.xml @@ -1,59 +1,56 @@ + - - + MuPDF + 1.7 (git build) Storage media not present - Sharing the storage media with a PC can make it inaccessible - Cancel - Search backwards - Search forwards - Search document - %1$s %2$s: %3$s - Table of Contents - Enter password - Text not found - Searching… - Highlight and enable links - No further occurrences found - Select - Search - Copy - Strike-out - Delete - Highlight - Underline - Edit annotations - Ink - Save - Print - Dismiss - [Up one level] - Yes - No - Entering reflow mode - Leaving reflow mode - Print failed - Select text - Copied to clipboard - No text selected - Draw annotation - Nothing to save - Document has changes. Save them? - Cannot open document - Cannot open document: %1$s - Cannot open file: %1$s - Cannot open buffer - Fill out text field - Okay - Choose value - Not supported - Copy text to the clipboard - More - Accept - copy text - Format currently not supported + Sharing the storage media with a PC can make it inaccessible + Cancel + Search backwards + Search forwards + Search document + %1$s %2$s: %3$s + Table of Contents + Enter password + Text not found + Searching… + Highlight and enable links + No further occurrences found + Select + Search + Copy + Strike-out + Delete + Highlight + Underline + Edit annotations + Ink + Save + Print + Dismiss + [Up one level] + Yes + No + Entering reflow mode + Leaving reflow mode + Print failed + Select text + Copied to clipboard + No text selected + Draw annotation + Nothing to save + Document has changes. Save them? + Cannot open document + Cannot open document: %1$s + Cannot open file: %1$s + Cannot open buffer + Fill out text field + Okay + Choose value + Not supported + Copy text to the clipboard + More + Accept + copy text + Format currently not supported Toggle reflow mode - - %1$d of %2$d - %1$d-%2$d of %3$d - diff --git a/android/platform/android/res/values/styles.xml b/android/platform/android/res/values/styles.xml deleted file mode 100644 index 6ce89c7..0000000 --- a/android/platform/android/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - diff --git a/android/platform/android/res/values/two_way_view_attrs.xml b/android/platform/android/res/values/two_way_view_attrs.xml deleted file mode 100644 index 78715e9..0000000 --- a/android/platform/android/res/values/two_way_view_attrs.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/src/com/artifex/mupdflib/Annotation.java b/android/src/com/artifex/mupdflib/Annotation.java index 98fd301..d70d29c 100644 --- a/android/src/com/artifex/mupdflib/Annotation.java +++ b/android/src/com/artifex/mupdflib/Annotation.java @@ -4,7 +4,9 @@ public class Annotation extends RectF { enum Type { - TEXT, LINK, FREETEXT, LINE, SQUARE, CIRCLE, POLYGON, POLYLINE, HIGHLIGHT, UNDERLINE, SQUIGGLY, STRIKEOUT, STAMP, CARET, INK, POPUP, FILEATTACHMENT, SOUND, MOVIE, WIDGET, SCREEN, PRINTERMARK, TRAPNET, WATERMARK, A3D, UNKNOWN + TEXT, LINK, FREETEXT, LINE, SQUARE, CIRCLE, POLYGON, POLYLINE, HIGHLIGHT, + UNDERLINE, SQUIGGLY, STRIKEOUT, STAMP, CARET, INK, POPUP, FILEATTACHMENT, + SOUND, MOVIE, WIDGET, SCREEN, PRINTERMARK, TRAPNET, WATERMARK, A3D, UNKNOWN } public final Type type; diff --git a/android/src/com/artifex/mupdflib/ArrayDeque.java b/android/src/com/artifex/mupdflib/ArrayDeque.java index e0dad57..a76fdbf 100644 --- a/android/src/com/artifex/mupdflib/ArrayDeque.java +++ b/android/src/com/artifex/mupdflib/ArrayDeque.java @@ -5,7 +5,16 @@ package com.artifex.mupdflib; -import java.util.*; +import java.util.AbstractCollection; +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Stack; // BEGIN android-note // removed link to collections framework docs diff --git a/android/src/com/artifex/mupdflib/AsyncTask.java b/android/src/com/artifex/mupdflib/AsyncTask.java index e94b634..13a68c9 100644 --- a/android/src/com/artifex/mupdflib/AsyncTask.java +++ b/android/src/com/artifex/mupdflib/AsyncTask.java @@ -16,728 +16,655 @@ package com.artifex.mupdflib; -import android.os.Handler; -import android.os.Message; -import android.os.Process; - -import java.util.concurrent.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.FutureTask; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import android.os.Process; +import android.os.Handler; +import android.os.Message; + /** - *

- * AsyncTask enables proper and easy use of the UI thread. This class allows to + *

AsyncTask enables proper and easy use of the UI thread. This class allows to * perform background operations and publish results on the UI thread without - * having to manipulate threads and/or handlers. - *

- * - *

- * AsyncTask is designed to be a helper class around {@link Thread} and - * {@link Handler} and does not constitute a generic threading framework. - * AsyncTasks should ideally be used for short operations (a few seconds at the - * most.) If you need to keep threads running for long periods of time, it is - * highly recommended you use the various APIs provided by the - * java.util.concurrent pacakge such as {@link Executor}, - * {@link ThreadPoolExecutor} and {@link FutureTask}. - *

- * - *

- * An asynchronous task is defined by a computation that runs on a background - * thread and whose result is published on the UI thread. An asynchronous task - * is defined by 3 generic types, called Params, - * Progress and Result, and 4 steps, called - * onPreExecute, doInBackground, - * onProgressUpdate and onPostExecute. - *

- * - *

Developer Guides

- *

- * For more information about using tasks and threads, read the Processes and - * Threads developer guide. - *

+ * having to manipulate threads and/or handlers.

+ * + *

AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} + * and does not constitute a generic threading framework. AsyncTasks should ideally be + * used for short operations (a few seconds at the most.) If you need to keep threads + * running for long periods of time, it is highly recommended you use the various APIs + * provided by the java.util.concurrent pacakge such as {@link Executor}, + * {@link ThreadPoolExecutor} and {@link FutureTask}.

+ * + *

An asynchronous task is defined by a computation that runs on a background thread and + * whose result is published on the UI thread. An asynchronous task is defined by 3 generic + * types, called Params, Progress and Result, + * and 4 steps, called onPreExecute, doInBackground, + * onProgressUpdate and onPostExecute.

+ * + *
+ *

Developer Guides

+ *

For more information about using tasks and threads, read the + * Processes and + * Threads developer guide.

*
- * + * *

Usage

- *

- * AsyncTask must be subclassed to be used. The subclass will override at least - * one method ({@link #doInBackground}), and most often will override a second - * one ({@link #onPostExecute}.) - *

- * - *

- * Here is an example of subclassing: - *

- * + *

AsyncTask must be subclassed to be used. The subclass will override at least + * one method ({@link #doInBackground}), and most often will override a + * second one ({@link #onPostExecute}.)

+ * + *

Here is an example of subclassing:

*
  * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
- * 	protected Long doInBackground(URL... urls) {
- * 		int count = urls.length;
- * 		long totalSize = 0;
- * 		for (int i = 0; i < count; i++) {
- * 			totalSize += Downloader.downloadFile(urls[i]);
- * 			publishProgress((int) ((i / (float) count) * 100));
- * 			// Escape early if cancel() is called
- * 			if (isCancelled())
- * 				break;
- * 		}
- * 		return totalSize;
- * 	}
- * 
- * 	protected void onProgressUpdate(Integer... progress) {
- * 		setProgressPercent(progress[0]);
- * 	}
- * 
- * 	protected void onPostExecute(Long result) {
- * 		showDialog("Downloaded " + result + " bytes");
- * 	}
+ *     protected Long doInBackground(URL... urls) {
+ *         int count = urls.length;
+ *         long totalSize = 0;
+ *         for (int i = 0; i < count; i++) {
+ *             totalSize += Downloader.downloadFile(urls[i]);
+ *             publishProgress((int) ((i / (float) count) * 100));
+ *             // Escape early if cancel() is called
+ *             if (isCancelled()) break;
+ *         }
+ *         return totalSize;
+ *     }
+ *
+ *     protected void onProgressUpdate(Integer... progress) {
+ *         setProgressPercent(progress[0]);
+ *     }
+ *
+ *     protected void onPostExecute(Long result) {
+ *         showDialog("Downloaded " + result + " bytes");
+ *     }
  * }
  * 
- * - *

- * Once created, a task is executed very simply: - *

- * + * + *

Once created, a task is executed very simply:

*
  * new DownloadFilesTask().execute(url1, url2, url3);
  * 
- * + * *

AsyncTask's generic types

- *

- * The three types used by an asynchronous task are the following: - *

+ *

The three types used by an asynchronous task are the following:

*
    - *
  1. Params, the type of the parameters sent to the task upon - * execution.
  2. - *
  3. Progress, the type of the progress units published during - * the background computation.
  4. - *
  5. Result, the type of the result of the background - * computation.
  6. + *
  7. Params, the type of the parameters sent to the task upon + * execution.
  8. + *
  9. Progress, the type of the progress units published during + * the background computation.
  10. + *
  11. Result, the type of the result of the background + * computation.
  12. *
- *

- * Not all types are always used by an asynchronous task. To mark a type as - * unused, simply use the type {@link Void}: - *

- * + *

Not all types are always used by an asynchronous task. To mark a type as unused, + * simply use the type {@link Void}:

*
  * private class MyTask extends AsyncTask<Void, Void, Void> { ... }
  * 
- * + * *

The 4 steps

- *

- * When an asynchronous task is executed, the task goes through 4 steps: - *

+ *

When an asynchronous task is executed, the task goes through 4 steps:

*
    - *
  1. {@link #onPreExecute()}, invoked on the UI thread before the task is - * executed. This step is normally used to setup the task, for instance by - * showing a progress bar in the user interface.
  2. - *
  3. {@link #doInBackground}, invoked on the background thread immediately - * after {@link #onPreExecute()} finishes executing. This step is used to - * perform background computation that can take a long time. The parameters of - * the asynchronous task are passed to this step. The result of the computation - * must be returned by this step and will be passed back to the last step. This - * step can also use {@link #publishProgress} to publish one or more units of - * progress. These values are published on the UI thread, in the - * {@link #onProgressUpdate} step.
  4. - *
  5. {@link #onProgressUpdate}, invoked on the UI thread after a call to - * {@link #publishProgress}. The timing of the execution is undefined. This - * method is used to display any form of progress in the user interface while - * the background computation is still executing. For instance, it can be used - * to animate a progress bar or show logs in a text field.
  6. - *
  7. {@link #onPostExecute}, invoked on the UI thread after the background - * computation finishes. The result of the background computation is passed to - * this step as a parameter.
  8. + *
  9. {@link #onPreExecute()}, invoked on the UI thread before the task + * is executed. This step is normally used to setup the task, for instance by + * showing a progress bar in the user interface.
  10. + *
  11. {@link #doInBackground}, invoked on the background thread + * immediately after {@link #onPreExecute()} finishes executing. This step is used + * to perform background computation that can take a long time. The parameters + * of the asynchronous task are passed to this step. The result of the computation must + * be returned by this step and will be passed back to the last step. This step + * can also use {@link #publishProgress} to publish one or more units + * of progress. These values are published on the UI thread, in the + * {@link #onProgressUpdate} step.
  12. + *
  13. {@link #onProgressUpdate}, invoked on the UI thread after a + * call to {@link #publishProgress}. The timing of the execution is + * undefined. This method is used to display any form of progress in the user + * interface while the background computation is still executing. For instance, + * it can be used to animate a progress bar or show logs in a text field.
  14. + *
  15. {@link #onPostExecute}, invoked on the UI thread after the background + * computation finishes. The result of the background computation is passed to + * this step as a parameter.
  16. *
- * + * *

Cancelling a task

- *

- * A task can be cancelled at any time by invoking {@link #cancel(boolean)}. - * Invoking this method will cause subsequent calls to {@link #isCancelled()} to - * return true. After invoking this method, {@link #onCancelled(Object)}, - * instead of {@link #onPostExecute(Object)} will be invoked after - * {@link #doInBackground(Object[])} returns. To ensure that a task is cancelled - * as quickly as possible, you should always check the return value of - * {@link #isCancelled()} periodically from {@link #doInBackground(Object[])}, - * if possible (inside a loop for instance.) - *

- * + *

A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking + * this method will cause subsequent calls to {@link #isCancelled()} to return true. + * After invoking this method, {@link #onCancelled(Object)}, instead of + * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])} + * returns. To ensure that a task is cancelled as quickly as possible, you should always + * check the return value of {@link #isCancelled()} periodically from + * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)

+ * *

Threading rules

- *

- * There are a few threading rules that must be followed for this class to work - * properly: - *

+ *

There are a few threading rules that must be followed for this class to + * work properly:

*
    - *
  • The AsyncTask class must be loaded on the UI thread. This is done - * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.
  • - *
  • The task instance must be created on the UI thread.
  • - *
  • {@link #execute} must be invoked on the UI thread.
  • - *
  • Do not call {@link #onPreExecute()}, {@link #onPostExecute}, - * {@link #doInBackground}, {@link #onProgressUpdate} manually.
  • - *
  • The task can be executed only once (an exception will be thrown if a - * second execution is attempted.)
  • + *
  • The AsyncTask class must be loaded on the UI thread. This is done + * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.
  • + *
  • The task instance must be created on the UI thread.
  • + *
  • {@link #execute} must be invoked on the UI thread.
  • + *
  • Do not call {@link #onPreExecute()}, {@link #onPostExecute}, + * {@link #doInBackground}, {@link #onProgressUpdate} manually.
  • + *
  • The task can be executed only once (an exception will be thrown if + * a second execution is attempted.)
  • *
- * + * *

Memory observability

- *

- * AsyncTask guarantees that all callback calls are synchronized in such a way - * that the following operations are safe without explicit synchronizations. - *

+ *

AsyncTask guarantees that all callback calls are synchronized in such a way that the following + * operations are safe without explicit synchronizations.

*
    - *
  • Set member fields in the constructor or {@link #onPreExecute}, and refer - * to them in {@link #doInBackground}. - *
  • Set member fields in {@link #doInBackground}, and refer to them in - * {@link #onProgressUpdate} and {@link #onPostExecute}. + *
  • Set member fields in the constructor or {@link #onPreExecute}, and refer to them + * in {@link #doInBackground}. + *
  • Set member fields in {@link #doInBackground}, and refer to them in + * {@link #onProgressUpdate} and {@link #onPostExecute}. *
- * + * *

Order of execution

- *

- * When first introduced, AsyncTasks were executed serially on a single - * background thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT} - * , this was changed to a pool of threads allowing multiple tasks to operate in - * parallel. Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, - * tasks are executed on a single thread to avoid common application errors - * caused by parallel execution. - *

- *

- * If you truly want parallel execution, you can invoke + *

When first introduced, AsyncTasks were executed serially on a single background + * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed + * to a pool of threads allowing multiple tasks to operate in parallel. Starting with + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single + * thread to avoid common application errors caused by parallel execution.

+ *

If you truly want parallel execution, you can invoke * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with - * {@link #THREAD_POOL_EXECUTOR}. - *

+ * {@link #THREAD_POOL_EXECUTOR}.

*/ public abstract class AsyncTask { - private static final String LOG_TAG = "AsyncTask"; - - private static final int CORE_POOL_SIZE = 5; - private static final int MAXIMUM_POOL_SIZE = 128; - private static final int KEEP_ALIVE = 1; - - private static final ThreadFactory sThreadFactory = new ThreadFactory() { - private final AtomicInteger mCount = new AtomicInteger(1); - - public Thread newThread(Runnable r) { - return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); - } - }; - - private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue( - 10); - - /** - * An {@link Executor} that can be used to execute tasks in parallel. - */ - public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( - CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, - sPoolWorkQueue, sThreadFactory); - - /** - * An {@link Executor} that executes tasks one at a time in serial order. - * This serialization is global to a particular process. - */ - public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); - - private static final int MESSAGE_POST_RESULT = 0x1; - private static final int MESSAGE_POST_PROGRESS = 0x2; - - private static final InternalHandler sHandler = new InternalHandler(); - - private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; - private final WorkerRunnable mWorker; - private final FutureTask mFuture; - - private volatile Status mStatus = Status.PENDING; - - private final AtomicBoolean mCancelled = new AtomicBoolean(); - private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); - - private static class SerialExecutor implements Executor { - final ArrayDeque mTasks = new ArrayDeque(); - Runnable mActive; - - public synchronized void execute(final Runnable r) { - mTasks.offer(new Runnable() { - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - - protected synchronized void scheduleNext() { - if ((mActive = mTasks.poll()) != null) { - THREAD_POOL_EXECUTOR.execute(mActive); - } - } - } - - /** - * Indicates the current status of the task. Each status will be set only - * once during the lifetime of a task. - */ - public enum Status { - /** - * Indicates that the task has not been executed yet. - */ - PENDING, - /** - * Indicates that the task is running. - */ - RUNNING, - /** - * Indicates that {@link AsyncTask#onPostExecute} has finished. - */ - FINISHED, - } - - /** @hide Used to force static handler to be created. */ - public static void init() { - sHandler.getLooper(); - } - - /** @hide */ - public static void setDefaultExecutor(Executor exec) { - sDefaultExecutor = exec; - } - - /** - * Creates a new asynchronous task. This constructor must be invoked on the - * UI thread. - */ - public AsyncTask() { - mWorker = new WorkerRunnable() { - public Result call() throws Exception { - mTaskInvoked.set(true); - - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - // noinspection unchecked - return postResult(doInBackground(mParams)); - } - }; - - mFuture = new FutureTask(mWorker) { - @Override - protected void done() { - try { - postResultIfNotInvoked(get()); - } catch (InterruptedException e) { - android.util.Log.w(LOG_TAG, e); - } catch (ExecutionException e) { - throw new RuntimeException( - "An error occured while executing doInBackground()", - e.getCause()); - } catch (CancellationException e) { - postResultIfNotInvoked(null); - } - } - }; - } - - private void postResultIfNotInvoked(Result result) { - final boolean wasTaskInvoked = mTaskInvoked.get(); - if (!wasTaskInvoked) { - postResult(result); - } - } - - private Result postResult(Result result) { - @SuppressWarnings("unchecked") - Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, - new AsyncTaskResult(this, result)); - message.sendToTarget(); - return result; - } - - /** - * Returns the current status of this task. - * - * @return The current status. - */ - public final Status getStatus() { - return mStatus; - } - - /** - * Override this method to perform a computation on a background thread. The - * specified parameters are the parameters passed to {@link #execute} by the - * caller of this task. - * - * This method can call {@link #publishProgress} to publish updates on the - * UI thread. - * - * @param params - * The parameters of the task. - * - * @return A result, defined by the subclass of this task. - * - * @see #onPreExecute() - * @see #onPostExecute - * @see #publishProgress - */ - protected abstract Result doInBackground(Params... params); - - /** - * Runs on the UI thread before {@link #doInBackground}. - * - * @see #onPostExecute - * @see #doInBackground - */ - protected void onPreExecute() { - } - - /** - *

- * Runs on the UI thread after {@link #doInBackground}. The specified result - * is the value returned by {@link #doInBackground}. - *

- * - *

- * This method won't be invoked if the task was cancelled. - *

- * - * @param result - * The result of the operation computed by - * {@link #doInBackground}. - * - * @see #onPreExecute - * @see #doInBackground - * @see #onCancelled(Object) - */ - @SuppressWarnings({ "UnusedDeclaration" }) - protected void onPostExecute(Result result) { - } - - /** - * Runs on the UI thread after {@link #publishProgress} is invoked. The - * specified values are the values passed to {@link #publishProgress}. - * - * @param values - * The values indicating progress. - * - * @see #publishProgress - * @see #doInBackground - */ - @SuppressWarnings({ "UnusedDeclaration" }) - protected void onProgressUpdate(Progress... values) { - } - - /** - *

- * Runs on the UI thread after {@link #cancel(boolean)} is invoked and - * {@link #doInBackground(Object[])} has finished. - *

- * - *

- * The default implementation simply invokes {@link #onCancelled()} and - * ignores the result. If you write your own implementation, do not call - * super.onCancelled(result). - *

- * - * @param result - * The result, if any, computed in - * {@link #doInBackground(Object[])}, can be null - * - * @see #cancel(boolean) - * @see #isCancelled() - */ - @SuppressWarnings({ "UnusedParameters" }) - protected void onCancelled(Result result) { - onCancelled(); - } - - /** - *

- * Applications should preferably override {@link #onCancelled(Object)}. - * This method is invoked by the default implementation of - * {@link #onCancelled(Object)}. - *

- * - *

- * Runs on the UI thread after {@link #cancel(boolean)} is invoked and - * {@link #doInBackground(Object[])} has finished. - *

- * - * @see #onCancelled(Object) - * @see #cancel(boolean) - * @see #isCancelled() - */ - protected void onCancelled() { - } - - /** - * Returns true if this task was cancelled before it completed - * normally. If you are calling {@link #cancel(boolean)} on the task, the - * value returned by this method should be checked periodically from - * {@link #doInBackground(Object[])} to end the task as soon as possible. - * - * @return true if task was cancelled before it completed - * - * @see #cancel(boolean) - */ - public final boolean isCancelled() { - return mCancelled.get(); - } - - /** - *

- * Attempts to cancel execution of this task. This attempt will fail if the - * task has already completed, already been cancelled, or could not be - * cancelled for some other reason. If successful, and this task has not - * started when cancel is called, this task should never run. If - * the task has already started, then the mayInterruptIfRunning - * parameter determines whether the thread executing this task should be - * interrupted in an attempt to stop the task. - *

- * - *

- * Calling this method will result in {@link #onCancelled(Object)} being - * invoked on the UI thread after {@link #doInBackground(Object[])} returns. - * Calling this method guarantees that {@link #onPostExecute(Object)} is - * never invoked. After invoking this method, you should check the value - * returned by {@link #isCancelled()} periodically from - * {@link #doInBackground(Object[])} to finish the task as early as - * possible. - *

- * - * @param mayInterruptIfRunning - * true if the thread executing this task should be - * interrupted; otherwise, in-progress tasks are allowed to - * complete. - * - * @return false if the task could not be cancelled, typically - * because it has already completed normally; true - * otherwise - * - * @see #isCancelled() - * @see #onCancelled(Object) - */ - public final boolean cancel(boolean mayInterruptIfRunning) { - mCancelled.set(true); - return mFuture.cancel(mayInterruptIfRunning); - } - - /** - * Waits if necessary for the computation to complete, and then retrieves - * its result. - * - * @return The computed result. - * - * @throws CancellationException - * If the computation was cancelled. - * @throws ExecutionException - * If the computation threw an exception. - * @throws InterruptedException - * If the current thread was interrupted while waiting. - */ - public final Result get() throws InterruptedException, ExecutionException { - return mFuture.get(); - } - - /** - * Waits if necessary for at most the given time for the computation to - * complete, and then retrieves its result. - * - * @param timeout - * Time to wait before cancelling the operation. - * @param unit - * The time unit for the timeout. - * - * @return The computed result. - * - * @throws CancellationException - * If the computation was cancelled. - * @throws ExecutionException - * If the computation threw an exception. - * @throws InterruptedException - * If the current thread was interrupted while waiting. - * @throws TimeoutException - * If the wait timed out. - */ - public final Result get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - return mFuture.get(timeout, unit); - } - - /** - * Executes the task with the specified parameters. The task returns itself - * (this) so that the caller can keep a reference to it. - * - *

- * Note: this function schedules the task on a queue for a single background - * thread or pool of threads depending on the platform version. When first - * introduced, AsyncTasks were executed serially on a single background - * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this - * was changed to a pool of threads allowing multiple tasks to operate in - * parallel. Starting {@link android.os.Build.VERSION_CODES#HONEYCOMB}, - * tasks are back to being executed on a single thread to avoid common - * application errors caused by parallel execution. If you truly want - * parallel execution, you can use the {@link #executeOnExecutor} version of - * this method with {@link #THREAD_POOL_EXECUTOR}; however, see commentary - * there for warnings on its use. - * - *

- * This method must be invoked on the UI thread. - * - * @param params - * The parameters of the task. - * - * @return This instance of AsyncTask. - * - * @throws IllegalStateException - * If {@link #getStatus()} returns either - * {@link AsyncTask.Status#RUNNING} or - * {@link AsyncTask.Status#FINISHED}. - * - * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) - * @see #execute(Runnable) - */ - public final AsyncTask execute(Params... params) { - return executeOnExecutor(sDefaultExecutor, params); - } - - /** - * Executes the task with the specified parameters. The task returns itself - * (this) so that the caller can keep a reference to it. - * - *

- * This method is typically used with {@link #THREAD_POOL_EXECUTOR} to allow - * multiple tasks to run in parallel on a pool of threads managed by - * AsyncTask, however you can also use your own {@link Executor} for custom - * behavior. - * - *

- * Warning: Allowing multiple tasks to run in parallel from a - * thread pool is generally not what one wants, because the order - * of their operation is not defined. For example, if these tasks are used - * to modify any state in common (such as writing a file due to a button - * click), there are no guarantees on the order of the modifications. - * Without careful work it is possible in rare cases for the newer version - * of the data to be over-written by an older one, leading to obscure data - * loss and stability issues. Such changes are best executed in serial; to - * guarantee such work is serialized regardless of platform version you can - * use this function with {@link #SERIAL_EXECUTOR}. - * - *

- * This method must be invoked on the UI thread. - * - * @param exec - * The executor to use. {@link #THREAD_POOL_EXECUTOR} is - * available as a convenient process-wide thread pool for tasks - * that are loosely coupled. - * @param params - * The parameters of the task. - * - * @return This instance of AsyncTask. - * - * @throws IllegalStateException - * If {@link #getStatus()} returns either - * {@link AsyncTask.Status#RUNNING} or - * {@link AsyncTask.Status#FINISHED}. - * - * @see #execute(Object[]) - */ - public final AsyncTask executeOnExecutor( - Executor exec, Params... params) { - if (mStatus != Status.PENDING) { - switch (mStatus) { - case RUNNING: - throw new IllegalStateException("Cannot execute task:" - + " the task is already running."); - case FINISHED: - throw new IllegalStateException("Cannot execute task:" - + " the task has already been executed " - + "(a task can be executed only once)"); - } - } - - mStatus = Status.RUNNING; - - onPreExecute(); - - mWorker.mParams = params; - exec.execute(mFuture); - - return this; - } - - /** - * Convenience version of {@link #execute(Object...)} for use with a simple - * Runnable object. See {@link #execute(Object[])} for more information on - * the order of execution. - * - * @see #execute(Object[]) - * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) - */ - public static void execute(Runnable runnable) { - sDefaultExecutor.execute(runnable); - } - - /** - * This method can be invoked from {@link #doInBackground} to publish - * updates on the UI thread while the background computation is still - * running. Each call to this method will trigger the execution of - * {@link #onProgressUpdate} on the UI thread. - * - * {@link #onProgressUpdate} will note be called if the task has been - * canceled. - * - * @param values - * The progress values to update the UI with. - * - * @see #onProgressUpdate - * @see #doInBackground - */ - protected final void publishProgress(Progress... values) { - if (!isCancelled()) { - sHandler.obtainMessage(MESSAGE_POST_PROGRESS, - new AsyncTaskResult(this, values)).sendToTarget(); - } - } - - private void finish(Result result) { - if (isCancelled()) { - onCancelled(result); - } else { - onPostExecute(result); - } - mStatus = Status.FINISHED; - } - - private static class InternalHandler extends Handler { - @SuppressWarnings({ "unchecked", "RawUseOfParameterizedType" }) - @Override - public void handleMessage(Message msg) { - AsyncTaskResult result = (AsyncTaskResult) msg.obj; - switch (msg.what) { - case MESSAGE_POST_RESULT: - // There is only one result - result.mTask.finish(result.mData[0]); - break; - case MESSAGE_POST_PROGRESS: - result.mTask.onProgressUpdate(result.mData); - break; - } - } - } - - private static abstract class WorkerRunnable implements - Callable { - Params[] mParams; - } - - @SuppressWarnings({ "RawUseOfParameterizedType" }) - private static class AsyncTaskResult { - final AsyncTask mTask; - final Data[] mData; - - AsyncTaskResult(AsyncTask task, Data... data) { - mTask = task; - mData = data; - } - } + private static final String LOG_TAG = "AsyncTask"; + + private static final int CORE_POOL_SIZE = 5; + private static final int MAXIMUM_POOL_SIZE = 128; + private static final int KEEP_ALIVE = 1; + + private static final ThreadFactory sThreadFactory = new ThreadFactory() { + private final AtomicInteger mCount = new AtomicInteger(1); + + public Thread newThread(Runnable r) { + return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); + } + }; + + private static final BlockingQueue sPoolWorkQueue = + new LinkedBlockingQueue(10); + + /** + * An {@link Executor} that can be used to execute tasks in parallel. + */ + public static final Executor THREAD_POOL_EXECUTOR + = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, + TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); + + /** + * An {@link Executor} that executes tasks one at a time in serial + * order. This serialization is global to a particular process. + */ + public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); + + private static final int MESSAGE_POST_RESULT = 0x1; + private static final int MESSAGE_POST_PROGRESS = 0x2; + + private static final InternalHandler sHandler = new InternalHandler(); + + private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; + private final WorkerRunnable mWorker; + private final FutureTask mFuture; + + private volatile Status mStatus = Status.PENDING; + + private final AtomicBoolean mCancelled = new AtomicBoolean(); + private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); + + private static class SerialExecutor implements Executor { + final ArrayDeque mTasks = new ArrayDeque(); + Runnable mActive; + + public synchronized void execute(final Runnable r) { + mTasks.offer(new Runnable() { + public void run() { + try { + r.run(); + } finally { + scheduleNext(); + } + } + }); + if (mActive == null) { + scheduleNext(); + } + } + + protected synchronized void scheduleNext() { + if ((mActive = mTasks.poll()) != null) { + THREAD_POOL_EXECUTOR.execute(mActive); + } + } + } + + /** + * Indicates the current status of the task. Each status will be set only once + * during the lifetime of a task. + */ + public enum Status { + /** + * Indicates that the task has not been executed yet. + */ + PENDING, + /** + * Indicates that the task is running. + */ + RUNNING, + /** + * Indicates that {@link AsyncTask#onPostExecute} has finished. + */ + FINISHED, + } + + /** @hide Used to force static handler to be created. */ + public static void init() { + sHandler.getLooper(); + } + + /** @hide */ + public static void setDefaultExecutor(Executor exec) { + sDefaultExecutor = exec; + } + + /** + * Creates a new asynchronous task. This constructor must be invoked on the UI thread. + */ + public AsyncTask() { + mWorker = new WorkerRunnable() { + public Result call() throws Exception { + mTaskInvoked.set(true); + + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + //noinspection unchecked + return postResult(doInBackground(mParams)); + } + }; + + mFuture = new FutureTask(mWorker) { + @Override + protected void done() { + try { + postResultIfNotInvoked(get()); + } catch (InterruptedException e) { + android.util.Log.w(LOG_TAG, e); + } catch (ExecutionException e) { + throw new RuntimeException("An error occured while executing doInBackground()", + e.getCause()); + } catch (CancellationException e) { + postResultIfNotInvoked(null); + } + } + }; + } + + private void postResultIfNotInvoked(Result result) { + final boolean wasTaskInvoked = mTaskInvoked.get(); + if (!wasTaskInvoked) { + postResult(result); + } + } + + private Result postResult(Result result) { + @SuppressWarnings("unchecked") + Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, + new AsyncTaskResult(this, result)); + message.sendToTarget(); + return result; + } + + /** + * Returns the current status of this task. + * + * @return The current status. + */ + public final Status getStatus() { + return mStatus; + } + + /** + * Override this method to perform a computation on a background thread. The + * specified parameters are the parameters passed to {@link #execute} + * by the caller of this task. + * + * This method can call {@link #publishProgress} to publish updates + * on the UI thread. + * + * @param params The parameters of the task. + * + * @return A result, defined by the subclass of this task. + * + * @see #onPreExecute() + * @see #onPostExecute + * @see #publishProgress + */ + protected abstract Result doInBackground(Params... params); + + /** + * Runs on the UI thread before {@link #doInBackground}. + * + * @see #onPostExecute + * @see #doInBackground + */ + protected void onPreExecute() { + } + + /** + *

Runs on the UI thread after {@link #doInBackground}. The + * specified result is the value returned by {@link #doInBackground}.

+ * + *

This method won't be invoked if the task was cancelled.

+ * + * @param result The result of the operation computed by {@link #doInBackground}. + * + * @see #onPreExecute + * @see #doInBackground + * @see #onCancelled(Object) + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected void onPostExecute(Result result) { + } + + /** + * Runs on the UI thread after {@link #publishProgress} is invoked. + * The specified values are the values passed to {@link #publishProgress}. + * + * @param values The values indicating progress. + * + * @see #publishProgress + * @see #doInBackground + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected void onProgressUpdate(Progress... values) { + } + + /** + *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and + * {@link #doInBackground(Object[])} has finished.

+ * + *

The default implementation simply invokes {@link #onCancelled()} and + * ignores the result. If you write your own implementation, do not call + * super.onCancelled(result).

+ * + * @param result The result, if any, computed in + * {@link #doInBackground(Object[])}, can be null + * + * @see #cancel(boolean) + * @see #isCancelled() + */ + @SuppressWarnings({"UnusedParameters"}) + protected void onCancelled(Result result) { + onCancelled(); + } + + /** + *

Applications should preferably override {@link #onCancelled(Object)}. + * This method is invoked by the default implementation of + * {@link #onCancelled(Object)}.

+ * + *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and + * {@link #doInBackground(Object[])} has finished.

+ * + * @see #onCancelled(Object) + * @see #cancel(boolean) + * @see #isCancelled() + */ + protected void onCancelled() { + } + + /** + * Returns true if this task was cancelled before it completed + * normally. If you are calling {@link #cancel(boolean)} on the task, + * the value returned by this method should be checked periodically from + * {@link #doInBackground(Object[])} to end the task as soon as possible. + * + * @return true if task was cancelled before it completed + * + * @see #cancel(boolean) + */ + public final boolean isCancelled() { + return mCancelled.get(); + } + + /** + *

Attempts to cancel execution of this task. This attempt will + * fail if the task has already completed, already been cancelled, + * or could not be cancelled for some other reason. If successful, + * and this task has not started when cancel is called, + * this task should never run. If the task has already started, + * then the mayInterruptIfRunning parameter determines + * whether the thread executing this task should be interrupted in + * an attempt to stop the task.

+ * + *

Calling this method will result in {@link #onCancelled(Object)} being + * invoked on the UI thread after {@link #doInBackground(Object[])} + * returns. Calling this method guarantees that {@link #onPostExecute(Object)} + * is never invoked. After invoking this method, you should check the + * value returned by {@link #isCancelled()} periodically from + * {@link #doInBackground(Object[])} to finish the task as early as + * possible.

+ * + * @param mayInterruptIfRunning true if the thread executing this + * task should be interrupted; otherwise, in-progress tasks are allowed + * to complete. + * + * @return false if the task could not be cancelled, + * typically because it has already completed normally; + * true otherwise + * + * @see #isCancelled() + * @see #onCancelled(Object) + */ + public final boolean cancel(boolean mayInterruptIfRunning) { + mCancelled.set(true); + return mFuture.cancel(mayInterruptIfRunning); + } + + /** + * Waits if necessary for the computation to complete, and then + * retrieves its result. + * + * @return The computed result. + * + * @throws CancellationException If the computation was cancelled. + * @throws ExecutionException If the computation threw an exception. + * @throws InterruptedException If the current thread was interrupted + * while waiting. + */ + public final Result get() throws InterruptedException, ExecutionException { + return mFuture.get(); + } + + /** + * Waits if necessary for at most the given time for the computation + * to complete, and then retrieves its result. + * + * @param timeout Time to wait before cancelling the operation. + * @param unit The time unit for the timeout. + * + * @return The computed result. + * + * @throws CancellationException If the computation was cancelled. + * @throws ExecutionException If the computation threw an exception. + * @throws InterruptedException If the current thread was interrupted + * while waiting. + * @throws TimeoutException If the wait timed out. + */ + public final Result get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + return mFuture.get(timeout, unit); + } + + /** + * Executes the task with the specified parameters. The task returns + * itself (this) so that the caller can keep a reference to it. + * + *

Note: this function schedules the task on a queue for a single background + * thread or pool of threads depending on the platform version. When first + * introduced, AsyncTasks were executed serially on a single background thread. + * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed + * to a pool of threads allowing multiple tasks to operate in parallel. Starting + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being + * executed on a single thread to avoid common application errors caused + * by parallel execution. If you truly want parallel execution, you can use + * the {@link #executeOnExecutor} version of this method + * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings + * on its use. + * + *

This method must be invoked on the UI thread. + * + * @param params The parameters of the task. + * + * @return This instance of AsyncTask. + * + * @throws IllegalStateException If {@link #getStatus()} returns either + * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. + * + * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) + * @see #execute(Runnable) + */ + public final AsyncTask execute(Params... params) { + return executeOnExecutor(sDefaultExecutor, params); + } + + /** + * Executes the task with the specified parameters. The task returns + * itself (this) so that the caller can keep a reference to it. + * + *

This method is typically used with {@link #THREAD_POOL_EXECUTOR} to + * allow multiple tasks to run in parallel on a pool of threads managed by + * AsyncTask, however you can also use your own {@link Executor} for custom + * behavior. + * + *

Warning: Allowing multiple tasks to run in parallel from + * a thread pool is generally not what one wants, because the order + * of their operation is not defined. For example, if these tasks are used + * to modify any state in common (such as writing a file due to a button click), + * there are no guarantees on the order of the modifications. + * Without careful work it is possible in rare cases for the newer version + * of the data to be over-written by an older one, leading to obscure data + * loss and stability issues. Such changes are best + * executed in serial; to guarantee such work is serialized regardless of + * platform version you can use this function with {@link #SERIAL_EXECUTOR}. + * + *

This method must be invoked on the UI thread. + * + * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a + * convenient process-wide thread pool for tasks that are loosely coupled. + * @param params The parameters of the task. + * + * @return This instance of AsyncTask. + * + * @throws IllegalStateException If {@link #getStatus()} returns either + * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. + * + * @see #execute(Object[]) + */ + public final AsyncTask executeOnExecutor(Executor exec, + Params... params) { + if (mStatus != Status.PENDING) { + switch (mStatus) { + case RUNNING: + throw new IllegalStateException("Cannot execute task:" + + " the task is already running."); + case FINISHED: + throw new IllegalStateException("Cannot execute task:" + + " the task has already been executed " + + "(a task can be executed only once)"); + } + } + + mStatus = Status.RUNNING; + + onPreExecute(); + + mWorker.mParams = params; + exec.execute(mFuture); + + return this; + } + + /** + * Convenience version of {@link #execute(Object...)} for use with + * a simple Runnable object. See {@link #execute(Object[])} for more + * information on the order of execution. + * + * @see #execute(Object[]) + * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) + */ + public static void execute(Runnable runnable) { + sDefaultExecutor.execute(runnable); + } + + /** + * This method can be invoked from {@link #doInBackground} to + * publish updates on the UI thread while the background computation is + * still running. Each call to this method will trigger the execution of + * {@link #onProgressUpdate} on the UI thread. + * + * {@link #onProgressUpdate} will note be called if the task has been + * canceled. + * + * @param values The progress values to update the UI with. + * + * @see #onProgressUpdate + * @see #doInBackground + */ + protected final void publishProgress(Progress... values) { + if (!isCancelled()) { + sHandler.obtainMessage(MESSAGE_POST_PROGRESS, + new AsyncTaskResult(this, values)).sendToTarget(); + } + } + + private void finish(Result result) { + if (isCancelled()) { + onCancelled(result); + } else { + onPostExecute(result); + } + mStatus = Status.FINISHED; + } + + private static class InternalHandler extends Handler { + @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) + @Override + public void handleMessage(Message msg) { + AsyncTaskResult result = (AsyncTaskResult) msg.obj; + switch (msg.what) { + case MESSAGE_POST_RESULT: + // There is only one result + result.mTask.finish(result.mData[0]); + break; + case MESSAGE_POST_PROGRESS: + result.mTask.onProgressUpdate(result.mData); + break; + } + } + } + + private static abstract class WorkerRunnable implements Callable { + Params[] mParams; + } + + @SuppressWarnings({"RawUseOfParameterizedType"}) + private static class AsyncTaskResult { + final AsyncTask mTask; + final Data[] mData; + + AsyncTaskResult(AsyncTask task, Data... data) { + mTask = task; + mData = data; + } + } } diff --git a/android/src/com/artifex/mupdflib/CallbackApplication.java b/android/src/com/artifex/mupdflib/CallbackApplication.java deleted file mode 100644 index 46f7463..0000000 --- a/android/src/com/artifex/mupdflib/CallbackApplication.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.artifex.mupdflib; - - -public abstract class CallbackApplication { - - public interface MuPDFCallbackInterface { - public void callbackMethod(String str); - } - - public final static class MuPDFCallbackClass{ - private static String gaiStr = ""; - public static MuPDFCallbackInterface callback; - public MuPDFCallbackClass(){} - static void sendGaiView(String str) { - if (callback!=null) { - gaiStr = str; - callback.callbackMethod(gaiStr); - } - } - } - - -} - diff --git a/android/src/com/artifex/mupdflib/CancellableAsyncTask.java b/android/src/com/artifex/mupdflib/CancellableAsyncTask.java index aa39216..cd63d4b 100644 --- a/android/src/com/artifex/mupdflib/CancellableAsyncTask.java +++ b/android/src/com/artifex/mupdflib/CancellableAsyncTask.java @@ -76,4 +76,4 @@ public void execute(Params ... params) asyncTask.execute(params); } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/CancellableTaskDefinition.java b/android/src/com/artifex/mupdflib/CancellableTaskDefinition.java index 6dbd728..70927f7 100644 --- a/android/src/com/artifex/mupdflib/CancellableTaskDefinition.java +++ b/android/src/com/artifex/mupdflib/CancellableTaskDefinition.java @@ -5,4 +5,4 @@ public interface CancellableTaskDefinition public Result doInBackground(Params ... params); public void doCancel(); public void doCleanup(); -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/ChoosePDFItem.java b/android/src/com/artifex/mupdflib/ChoosePDFItem.java new file mode 100644 index 0000000..6470c90 --- /dev/null +++ b/android/src/com/artifex/mupdflib/ChoosePDFItem.java @@ -0,0 +1,15 @@ +package com.artifex.mupdflib; + +public class ChoosePDFItem { + enum Type { + PARENT, DIR, DOC + } + + final public Type type; + final public String name; + + public ChoosePDFItem (Type t, String n) { + type = t; + name = n; + } +} diff --git a/android/src/com/artifex/mupdflib/Deque.java b/android/src/com/artifex/mupdflib/Deque.java index 85102be..c19a5a0 100644 --- a/android/src/com/artifex/mupdflib/Deque.java +++ b/android/src/com/artifex/mupdflib/Deque.java @@ -6,7 +6,12 @@ package com.artifex.mupdflib; -import java.util.*; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Stack; // BEGIN android-note // removed link to collections framework docs diff --git a/android/src/com/artifex/mupdflib/FilePicker.java b/android/src/com/artifex/mupdflib/FilePicker.java index c2228a4..9bb611e 100644 --- a/android/src/com/artifex/mupdflib/FilePicker.java +++ b/android/src/com/artifex/mupdflib/FilePicker.java @@ -3,7 +3,6 @@ import android.net.Uri; public abstract class FilePicker { - public interface FilePickerSupport { void performPickFor(FilePicker picker); } @@ -20,4 +19,3 @@ void pick() { abstract void onPick(Uri uri); } - diff --git a/android/src/com/artifex/mupdflib/LibraryUtils.java b/android/src/com/artifex/mupdflib/LibraryUtils.java deleted file mode 100644 index 087cfd3..0000000 --- a/android/src/com/artifex/mupdflib/LibraryUtils.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.artifex.mupdflib; - -import java.util.Locale; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.preference.PreferenceManager; - -public class LibraryUtils { - private static String PREF_APP_LANGUAGE = "prefKeyLanguage"; - private static Locale locale = null; - public static void reloadLocale(Context context) - { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); - String lang = sharedPrefs.getString(PREF_APP_LANGUAGE, "lv"); - if (sharedPrefs.getString(PREF_APP_LANGUAGE, "").equalsIgnoreCase("")) { - sharedPrefs.edit().putString(PREF_APP_LANGUAGE, lang).commit(); - } - Configuration newConfig = context.getResources().getConfiguration(); - - if (! newConfig.locale.getLanguage().equals(lang)) - { - locale = new Locale(lang); - } - - if (locale != null) - { - Locale.setDefault(locale); - newConfig.locale = locale; - context.getResources().updateConfiguration(newConfig, context.getResources().getDisplayMetrics()); - } - } -} diff --git a/android/src/com/artifex/mupdflib/LinkInfoRemote.java b/android/src/com/artifex/mupdflib/LinkInfoRemote.java index 8ae477e..a13c739 100644 --- a/android/src/com/artifex/mupdflib/LinkInfoRemote.java +++ b/android/src/com/artifex/mupdflib/LinkInfoRemote.java @@ -5,8 +5,7 @@ public class LinkInfoRemote extends LinkInfo { final public int pageNumber; final public boolean newWindow; - public LinkInfoRemote(float l, float t, float r, float b, String f, int p, - boolean n) { + public LinkInfoRemote(float l, float t, float r, float b, String f, int p, boolean n) { super(l, t, r, b); fileSpec = f; pageNumber = p; diff --git a/android/src/com/artifex/mupdflib/LinkInfoVisitor.java b/android/src/com/artifex/mupdflib/LinkInfoVisitor.java index 1dba754..683a4eb 100644 --- a/android/src/com/artifex/mupdflib/LinkInfoVisitor.java +++ b/android/src/com/artifex/mupdflib/LinkInfoVisitor.java @@ -2,8 +2,6 @@ abstract public class LinkInfoVisitor { public abstract void visitInternal(LinkInfoInternal li); - public abstract void visitExternal(LinkInfoExternal li); - public abstract void visitRemote(LinkInfoRemote li); } diff --git a/android/src/com/artifex/mupdflib/MD5.java b/android/src/com/artifex/mupdflib/MD5.java deleted file mode 100644 index e0f8da9..0000000 --- a/android/src/com/artifex/mupdflib/MD5.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.artifex.mupdflib; - -import java.io.*; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class MD5 { - - public static boolean checkMD5(String md5, File updateFile) - throws IOException { - if (md5 == null || md5 == "" || updateFile == null) { - return false; - } - - String calculatedDigest = calculateMD5(updateFile); - - if (calculatedDigest == null) { - return false; - } - return calculatedDigest.equalsIgnoreCase(md5); - } - - public static String calculateMD5(File updateFile) { - MessageDigest digest = null; - try { - digest = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - return null; - } - InputStream is = null; - try { - is = new FileInputStream(updateFile); - } catch (FileNotFoundException e) { - return null; - } - byte[] buffer = new byte[8192]; - int read = 0; - try { - while ((read = is.read(buffer)) > 0) { - digest.update(buffer, 0, read); - } - byte[] md5sum = digest.digest(); - BigInteger bigInt = new BigInteger(1, md5sum); - String output = bigInt.toString(16); - // Fill to 32 chars - output = String.format("%32s", output).replace(' ', '0'); - return output; - } catch (IOException e) { - throw new RuntimeException("Unable to process file for MD5", e); - } finally { - try { - is.close(); - } catch (IOException e) { - throw new RuntimeException( - "Unable to close input stream for MD5 calculation", e); - } - } - } - - public static String MD5Hash(String s) { - MessageDigest m = null; - - try { - m = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - return ""; - } - - m.update(s.getBytes(), 0, s.length()); - String hash = new BigInteger(1, m.digest()).toString(16); - return hash; - } -} diff --git a/android/src/com/artifex/mupdflib/MuPDFActivity.java b/android/src/com/artifex/mupdflib/MuPDFActivity.java deleted file mode 100644 index 1e2455d..0000000 --- a/android/src/com/artifex/mupdflib/MuPDFActivity.java +++ /dev/null @@ -1,1290 +0,0 @@ -package com.artifex.mupdflib; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.Color; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.TranslateAnimation; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.*; -import android.widget.AdapterView.OnItemClickListener; - -import com.artifex.mupdflib.CallbackApplication.MuPDFCallbackClass; -import com.artifex.mupdflib.TwoWayView.Orientation; - -import java.io.InputStream; -import java.util.concurrent.Executor; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - -//import android.text.method.PasswordTransformationMethod; -//import android.widget.SeekBar; - -class ThreadPerTaskExecutor implements Executor { - public void execute(Runnable r) { - new Thread(r).start(); - } -} - -public class MuPDFActivity extends Activity implements FilePicker.FilePickerSupport { - /* The core rendering instance */ - //enum TopBarMode {Main, Search, Annot, Delete, More, Accept}; - enum TopBarMode {Main, Search}; - //enum AcceptMode {Highlight, Underline, StrikeOut, Ink, CopyText}; - - //private final int OUTLINE_REQUEST = 0; - private final int PRINT_REQUEST = 1; - private final int FILEPICK_REQUEST = 2; - private final int PAGE_CHOICE_REQUEST = 3; - private MuPDFCore core; - private String mFileName; - private String mDocName; - private int mOrientation; - private MuPDFReaderView mDocView; - private View mButtonsView; - private boolean mButtonsVisible; - // private EditText mPasswordView; - // private TextView mFilenameView; - ///private SeekBar mPageSlider; - ///private int mPageSliderRes; - private TextView mPageNumberView; - private TextView mInfoView; - private ImageButton mSearchButton; - //private ImageButton mReflowButton; - //private ImageButton mOutlineButton; - //private ImageButton mMoreButton; - //private TextView mAnnotTypeText; - //private ImageButton mAnnotButton; - private ViewAnimator mTopBarSwitcher; - //private ImageButton mLinkButton; - private TopBarMode mTopBarMode = TopBarMode.Main; - //private AcceptMode mAcceptMode; - private ImageButton mSearchBack; - private ImageButton mSearchFwd; - private EditText mSearchText; - private SearchTask mSearchTask; - private AlertDialog.Builder mAlertBuilder; - //private boolean mLinkHighlight = false; - private final Handler mHandler = new Handler(); - private FrameLayout mPreviewBarHolder; - private TwoWayView mPreview; - private ToolbarPreviewAdapter pdfPreviewPagerAdapter; - private boolean mAlertsActive = false; - //private boolean mReflow = false; - private AsyncTask mAlertTask; - private AlertDialog mAlertDialog; - private FilePicker mFilePicker; - - public void createAlertWaiter() { - mAlertsActive = true; - // All mupdf library calls are performed on asynchronous tasks to avoid stalling - // the UI. Some calls can lead to javascript-invoked requests to display an - // alert dialog and collect a reply from the user. The task has to be blocked - // until the user's reply is received. This method creates an asynchronous task, - // the purpose of which is to wait of these requests and produce the dialog - // in response, while leaving the core blocked. When the dialog receives the - // user's response, it is sent to the core via replyToAlert, unblocking it. - // Another alert-waiting task is then created to pick up the next alert. - if (mAlertTask != null) { - mAlertTask.cancel(true); - mAlertTask = null; - } - if (mAlertDialog != null) { - mAlertDialog.cancel(); - mAlertDialog = null; - } - mAlertTask = new AsyncTask() { - - @Override - protected MuPDFAlert doInBackground(Void... arg0) { - if (!mAlertsActive) - return null; - - return core.waitForAlert(); - } - - @Override - protected void onPostExecute(final MuPDFAlert result) { - // core.waitForAlert may return null when shutting down - if (result == null) - return; - final MuPDFAlert.ButtonPressed pressed[] = new MuPDFAlert.ButtonPressed[3]; - for(int i = 0; i < 3; i++) - pressed[i] = MuPDFAlert.ButtonPressed.None; - DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mAlertDialog = null; - if (mAlertsActive) { - int index = 0; - switch (which) { - case AlertDialog.BUTTON1: index=0; break; - case AlertDialog.BUTTON2: index=1; break; - case AlertDialog.BUTTON3: index=2; break; - } - result.buttonPressed = pressed[index]; - // Send the user's response to the core, so that it can - // continue processing. - core.replyToAlert(result); - // Create another alert-waiter to pick up the next alert. - createAlertWaiter(); - } - } - }; - mAlertDialog = mAlertBuilder.create(); - mAlertDialog.setTitle(result.title); - mAlertDialog.setMessage(result.message); - switch (result.iconType) - { - case Error: - break; - case Warning: - break; - case Question: - break; - case Status: - break; - } - try { - switch (result.buttonGroupType) - { - case OkCancel: - mAlertDialog.setButton(AlertDialog.BUTTON2, getString(TiRHelper.getResource("string.cancel")), listener); - pressed[1] = MuPDFAlert.ButtonPressed.Cancel; - case Ok: - mAlertDialog.setButton(AlertDialog.BUTTON1, getString(TiRHelper.getResource("string.okay")), listener); - pressed[0] = MuPDFAlert.ButtonPressed.Ok; - break; - case YesNoCancel: - mAlertDialog.setButton(AlertDialog.BUTTON3, getString(TiRHelper.getResource("string.cancel")), listener); - pressed[2] = MuPDFAlert.ButtonPressed.Cancel; - case YesNo: - mAlertDialog.setButton(AlertDialog.BUTTON1, getString(TiRHelper.getResource("string.yes")), listener); - pressed[0] = MuPDFAlert.ButtonPressed.Yes; - mAlertDialog.setButton(AlertDialog.BUTTON2, getString(TiRHelper.getResource("string.no")), listener); - pressed[1] = MuPDFAlert.ButtonPressed.No; - break; - } - } catch( ResourceNotFoundException e ){} - mAlertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - mAlertDialog = null; - if (mAlertsActive) { - result.buttonPressed = MuPDFAlert.ButtonPressed.None; - core.replyToAlert(result); - createAlertWaiter(); - } - } - }); - - mAlertDialog.show(); - } - }; - - mAlertTask.executeOnExecutor(new ThreadPerTaskExecutor()); - } - - public void destroyAlertWaiter() { - mAlertsActive = false; - if (mAlertDialog != null) { - mAlertDialog.cancel(); - mAlertDialog = null; - } - if (mAlertTask != null) { - mAlertTask.cancel(true); - mAlertTask = null; - } - } - - private MuPDFCore openFile(String path) { - int lastSlashPos = path.lastIndexOf('/'); - mFileName = new String(lastSlashPos == -1 ? path - : path.substring(lastSlashPos + 1)); - System.out.println("Trying to open " + path); - try { - core = new MuPDFCore(this, path); - // New file: drop the old outline data - //OutlineActivityData.set(null); - PDFPreviewGridActivityData.set(null); - } catch (Exception e) { - System.out.println(e); - return null; - } - return core; - } - - //private MuPDFCore openBuffer(byte buffer[]) - private MuPDFCore openBuffer(byte buffer[], String magic) { - System.out.println("Trying to open byte buffer"); - try { - //core = new MuPDFCore(this, buffer); - core = new MuPDFCore(this, buffer, magic); - - // New file: drop the old outline data - //OutlineActivityData.set(null); - PDFPreviewGridActivityData.set(null); - } catch (Exception e) { - System.out.println(e); - return null; - } - return core; - } - - - @SuppressWarnings("unused") - private Context getContext() { - return this; - } - - private void setCurrentlyViewedPreview() { - int i = mDocView.getDisplayedViewIndex(); - if (core.getDisplayPages() == 2) { - i = (i * 2) - 1; - } - pdfPreviewPagerAdapter.setCurrentlyViewing(i); - centerPreviewAtPosition(i); - } - - public void centerPreviewAtPosition(int position) { - if (mPreview.getChildCount() > 0) { - View child = mPreview.getChildAt(0); - // assume all children the same width - int childmeasuredwidth = child.getMeasuredWidth(); - - if (childmeasuredwidth > 0) { - if (core.getDisplayPages() == 2) { - mPreview.setSelectionFromOffset(position, - (mPreview.getWidth() / 2) - (childmeasuredwidth)); - } else { - mPreview.setSelectionFromOffset(position, - (mPreview.getWidth() / 2) - - (childmeasuredwidth / 2)); - } - } else { - Log.e("centerOnPosition", "childmeasuredwidth = 0"); - } - } else { - Log.e("centerOnPosition", "childcount = 0"); - } - } - - - /** Called when the activity is first created. */ - @SuppressLint("StringFormatMatches") - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - LibraryUtils.reloadLocale(getApplicationContext()); - - mAlertBuilder = new AlertDialog.Builder(this); - - if (core == null) { - core = (MuPDFCore) getLastNonConfigurationInstance(); - - if (savedInstanceState != null && savedInstanceState.containsKey("FileName")) { - mFileName = savedInstanceState.getString("FileName"); - } - } - if (core == null) { - Intent intent = getIntent(); - byte buffer[] = null; - if (Intent.ACTION_VIEW.equals(intent.getAction())) { - Uri uri = intent.getData(); - System.out.println("URI to open is: " + uri); - if (uri.toString().startsWith("content://")) { - /* - // Handle view requests from the Transformer Prime's file - // manager - // Hopefully other file managers will use this same scheme, - // if not - // using explicit paths. - Cursor cursor = getContentResolver().query(uri, - new String[] { "_data" }, null, null, null); - if (cursor.moveToFirst()) { - String str = cursor.getString(0); - String reason = null; - if (str == null) { - try { - InputStream is = getContentResolver() - .openInputStream(uri); - int len = is.available(); - buffer = new byte[len]; - is.read(buffer, 0, len); - is.close(); - } catch (java.lang.OutOfMemoryError e) { - System.out - .println("Out of memory during buffer reading"); - reason = e.toString(); - } catch (Exception e) { - reason = e.toString(); - } - if (reason != null) { - buffer = null; - Resources res = getResources(); - AlertDialog alert = mAlertBuilder.create(); - setTitle(String.format(res.getString(R.string.cannot_open_document_Reason), reason)); - alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss), new DialogInterface.OnClickListener() { - public void onClick( - DialogInterface dialog, - int which) { - finish(); - } - }); - alert.setOnCancelListener(new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - finish(); - } - }); - - alert.show(); - return; - */ - - String reason = null; - try { - InputStream is = getContentResolver().openInputStream(uri); - int len = is.available(); - buffer = new byte[len]; - is.read(buffer, 0, len); - is.close(); - } - catch (java.lang.OutOfMemoryError e) { - System.out.println("Out of memory during buffer reading"); - reason = e.toString(); - } - catch (Exception e) { - System.out.println("Exception reading from stream: " + e); - - // Handle view requests from the Transformer Prime's file manager - // Hopefully other file managers will use this same scheme, if not - // using explicit paths. - // I'm hoping that this case below is no longer needed...but it's - // hard to test as the file manager seems to have changed in 4.x. - try { - Cursor cursor = getContentResolver().query(uri, new String[]{"_data"}, null, null, null); - if (cursor.moveToFirst()) { - String str = cursor.getString(0); - if (str == null) { - reason = "Couldn't parse data in intent"; - } - else { - uri = Uri.parse(str); - } - - } - //} else { - // uri = Uri.parse(str); - } - catch (Exception e2) { - System.out.println("Exception in Transformer Prime file manager code: " + e2); - reason = e2.toString(); - } - } - if (reason != null) { - buffer = null; - Resources res = getResources(); - AlertDialog alert = mAlertBuilder.create(); - try { - setTitle(String.format(res.getString(TiRHelper.getResource("string.cannot_open_document_Reason")), reason)); - alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(TiRHelper.getResource("string.dismiss")), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - } catch( ResourceNotFoundException e ){} - alert.show(); - return; - - } - } - if (buffer != null) { - //core = openBuffer(buffer); - core = openBuffer(buffer, intent.getType()); - - } else { - core = openFile(Uri.decode(uri.getEncodedPath())); - } - SearchTaskResult.set(null); - if (core.countPages() == 0) - core = null; - } - if (core != null && core.needsPassword()) { - // requestPassword(savedInstanceState); - // required password getteris from data - String password = intent.getStringExtra("password"); - core.authenticatePassword(password); - // return; - } - if (core != null && core.countPages() == 0) - { - core = null; - } - - } - if (core == null) { - AlertDialog alert = mAlertBuilder.create(); - try { - alert.setTitle(TiRHelper.getResource("string.cannot_open_document")); - alert.setButton(AlertDialog.BUTTON_POSITIVE, - getString(TiRHelper.getResource("string.dismiss")), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - } catch ( ResourceNotFoundException e ){} - alert.show(); - return; - } - - mOrientation = getResources().getConfiguration().orientation; - - if(mOrientation == Configuration.ORIENTATION_LANDSCAPE) { - core.setDisplayPages(2); - } else { - core.setDisplayPages(1); - } - - createUI(savedInstanceState); - } - - /* - * public void requestPassword(final Bundle savedInstanceState) { - * mPasswordView = new EditText(this); - * mPasswordView.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); - * mPasswordView.setTransformationMethod(new - * PasswordTransformationMethod()); - * - * AlertDialog alert = mAlertBuilder.create(); - * alert.setTitle(R.string.enter_password); alert.setView(mPasswordView); - * alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.ok), new - * DialogInterface.OnClickListener() { public void onClick(DialogInterface - * dialog, int which) { if - * (core.authenticatePassword(mPasswordView.getText().toString())) { - * createUI(savedInstanceState); } else { - * requestPassword(savedInstanceState); } } }); - * alert.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), - * new DialogInterface.OnClickListener() { - * - * public void onClick(DialogInterface dialog, int which) { finish(); } }); - * alert.show(); } - */ - - public void createUI(Bundle savedInstanceState) { - if (core == null) - return; - - // Now create the UI. - // First create the document view - mDocView = new MuPDFReaderView(this) { - @Override - protected void onMoveToChild(int i) { - updatePageNumView(i); - //if (core == null) - // return; - //mPageNumberView.setText(String.format("%d / %d", i + 1, - // core.countPages())); - ///mPageSlider.setMax((core.countPages() - 1) * mPageSliderRes); - ///mPageSlider.setProgress(i * mPageSliderRes); - super.onMoveToChild(i); - setCurrentlyViewedPreview(); - - - } - - @Override - protected void onTapMainDocArea() { - if (!mButtonsVisible) { - showButtons(); - } else { - if (mTopBarMode == TopBarMode.Main) - hideButtons(); - } - } - - @Override - protected void onDocMotion() { - hideButtons(); - } - - @Override - protected void onHit(Hit item) { - /* - switch (mTopBarMode) { - case Annot: - if (item == Hit.Annotation) { - showButtons(); - mTopBarMode = TopBarMode.Delete; - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - break; - case Delete: - mTopBarMode = TopBarMode.Annot; - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - // fall through - default: - */ - // Not in annotation editing mode, but the pageview will - // still select and highlight hit annotations, so - // deselect just in case. - MuPDFView pageView = (MuPDFView) mDocView.getDisplayedView(); - if (pageView != null) - pageView.deselectAnnotation(); - //break; - //} - } - }; - mDocView.setAdapter(new MuPDFPageAdapter(this, this, core)); - - Intent intent = getIntent(); - boolean idleenabled = intent.getBooleanExtra("idleenabled", false); - boolean highlight = intent.getBooleanExtra("linkhighlight", false); - mDocView.setKeepScreenOn(!idleenabled); - mDocView.setLinksHighlighted(highlight); - mDocName = intent.getStringExtra("docname"); - - mSearchTask = new SearchTask(this, core) { - @Override - protected void onTextFound(SearchTaskResult result) { - SearchTaskResult.set(result); - // Ask the ReaderView to move to the resulting page - mDocView.setDisplayedViewIndex(result.pageNumber); - // Make the ReaderView act on the change to SearchTaskResult - // via overridden onChildSetup method. - mDocView.resetupChildren(); - } - }; - - // Make the buttons overlay, and store all its - // controls in variables - makeButtonsView(); - - // Set up the page slider - ///int smax = Math.max(core.countPages() - 1, 1); - ///mPageSliderRes = ((10 + smax - 1) / smax) * 2; - - // Set the file-name text - // /////////mFilenameView.setText(mFileName); - - // Activate the seekbar - /* - mPageSlider - .setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - public void onStopTrackingTouch(SeekBar seekBar) { - mDocView.setDisplayedViewIndex((seekBar.getProgress() + mPageSliderRes / 2) - / mPageSliderRes); - } - - public void onStartTrackingTouch(SeekBar seekBar) { - } - - public void onProgressChanged(SeekBar seekBar, - int progress, boolean fromUser) { - updatePageNumView((progress + mPageSliderRes / 2) - / mPageSliderRes); - } - }); - */ - // Activate the search-preparing button - mSearchButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - searchModeOn(); - } - }); - - // Activate the reflow button - /* - mReflowButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - toggleReflow(); - } - }); - */ - /* - if (core.fileFormat().startsWith("PDF")) { - mAnnotButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mTopBarMode = TopBarMode.Annot; - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - }); - } else { - mAnnotButton.setVisibility(View.GONE); - } - */ - // Search invoking buttons are disabled while there is no text specified - mSearchBack.setEnabled(false); - mSearchFwd.setEnabled(false); - mSearchBack.setColorFilter(Color.argb(255, 128, 128, 128)); - mSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128)); - - // React to interaction with the text widget - mSearchText.addTextChangedListener(new TextWatcher() { - - public void afterTextChanged(Editable s) { - boolean haveText = s.toString().length() > 0; - setButtonEnabled(mSearchBack, haveText); - setButtonEnabled(mSearchFwd, haveText); - - // Remove any previous search results - if (SearchTaskResult.get() != null - && !mSearchText.getText().toString().equals(SearchTaskResult.get().txt)) { - SearchTaskResult.set(null); - mDocView.resetupChildren(); - } - } - - public void beforeTextChanged(CharSequence s, int start, int count, - int after) { - } - - public void onTextChanged(CharSequence s, int start, int before, - int count) { - } - }); - - // React to Done button on keyboard - mSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - public boolean onEditorAction(TextView v, int actionId, - KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) - search(1); - return false; - } - }); - - mSearchText.setOnKeyListener(new View.OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && keyCode == KeyEvent.KEYCODE_ENTER) - search(1); - return false; - } - }); - - // Activate search invoking buttons - mSearchBack.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - search(-1); - } - }); - mSearchFwd.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - search(1); - } - }); - /* - mLinkButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - setLinkHighlight(!mLinkHighlight); - } - }); - */ - /* - if (core.hasOutline()) { - mOutlineButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - OutlineItem outline[] = core.getOutline(); - if (outline != null) { - OutlineActivityData.get().items = outline; - Intent intent = new Intent(MuPDFActivity.this,OutlineActivity.class); - startActivityForResult(intent, OUTLINE_REQUEST); - } - } - }); - } else { - mOutlineButton.setVisibility(View.GONE); - } - */ - // Reenstate last state if it was recorded - ///SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); - ///mDocView.setDisplayedViewIndex(prefs.getInt("page" + mFileName, 0)); - SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); - int orientation = prefs.getInt("orientation", mOrientation); - int pageNum = prefs.getInt("page"+mFileName, 0); - if(orientation == mOrientation) - mDocView.setDisplayedViewIndex(pageNum); - else { - if(orientation == Configuration.ORIENTATION_PORTRAIT) { - mDocView.setDisplayedViewIndex((pageNum + 1) / 2); - } else { - mDocView.setDisplayedViewIndex((pageNum == 0) ? 0 : pageNum * 2 - 1); - } - } - - //if (savedInstanceState == null - // || !savedInstanceState.getBoolean("ButtonsHidden", false)) - // showButtons(); - - if (savedInstanceState != null && savedInstanceState.getBoolean("SearchMode", false)) - searchModeOn(); - - //if (savedInstanceState != null && savedInstanceState.getBoolean("ReflowMode", false)) - //reflowModeSet(true); - - // Give preview thumbnails time to appear before showing bottom bar - if (savedInstanceState == null - || !savedInstanceState.getBoolean("ButtonsHidden", false)) { - mPreview.postDelayed(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - showButtons(); - } - }); - } - }, 250); - } - - // Stick the document view and the buttons overlay into a parent view - RelativeLayout layout = new RelativeLayout(this); - layout.addView(mDocView); - layout.addView(mButtonsView); - //layout.setBackgroundResource(R.drawable.tiled_background); - //layout.setBackgroundResource(R.color.canvas); - setContentView(layout); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - //case OUTLINE_REQUEST: - // if (resultCode >= 0) - // mDocView.setDisplayedViewIndex(resultCode); - // break; - case PRINT_REQUEST: - if (resultCode == RESULT_CANCELED) - try { - showInfo(getString(TiRHelper.getResource("string.print_failed"))); - } catch (ResourceNotFoundException e ){} - break; - case FILEPICK_REQUEST: - if (mFilePicker != null && resultCode == RESULT_OK) - mFilePicker.onPick(data.getData()); - break; - case PAGE_CHOICE_REQUEST: - if (resultCode >= 0) { - int page = resultCode; - if (core.getDisplayPages() == 2) { - page = (page + 1) / 2; - } - mDocView.setDisplayedViewIndex(page); - setCurrentlyViewedPreview(); - } - break; - } - super.onActivityResult(requestCode, resultCode, data); - } - - public Object onRetainNonConfigurationInstance() { - MuPDFCore mycore = core; - core = null; - return mycore; - } -/* - private void reflowModeSet(boolean reflow) { - mReflow = reflow; - mDocView.setAdapter(mReflow ? new MuPDFReflowAdapter(this, core) : new MuPDFPageAdapter(this, this, core)); - mReflowButton.setColorFilter(mReflow ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255)); - setButtonEnabled(mAnnotButton, !reflow); - setButtonEnabled(mSearchButton, !reflow); - if (reflow) - setLinkHighlight(false); - setButtonEnabled(mLinkButton, !reflow); - setButtonEnabled(mMoreButton, !reflow); - mDocView.refresh(mReflow); - } -*/ -/* - private void toggleReflow() { - reflowModeSet(!mReflow); - showInfo(mReflow ? getString(R.string.entering_reflow_mode) - : getString(R.string.leaving_reflow_mode)); - } -*/ - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - if (mFileName != null && mDocView != null) { - outState.putString("FileName", mFileName); - - // Store current page in the prefs against the file name, - // so that we can pick it up each time the file is loaded - // Other info is needed only for screen-orientation change, - // so it can go in the bundle - SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); - SharedPreferences.Editor edit = prefs.edit(); - edit.putInt("page" + mFileName, mDocView.getDisplayedViewIndex()); - edit.putInt("orientation", mOrientation); - edit.commit(); - } - - if (!mButtonsVisible) - outState.putBoolean("ButtonsHidden", true); - - if (mTopBarMode == TopBarMode.Search) - outState.putBoolean("SearchMode", true); - - //if (mReflow) - // outState.putBoolean("ReflowMode", true); - } - - @Override - protected void onPause() { - super.onPause(); - - if (mSearchTask != null) - mSearchTask.stop(); - - if (mFileName != null && mDocView != null) { - SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); - SharedPreferences.Editor edit = prefs.edit(); - edit.putInt("page" + mFileName, mDocView.getDisplayedViewIndex()); - edit.putInt("orientation", mOrientation); - edit.commit(); - } - } - - public void onDestroy() { - if (mDocView != null) { - mDocView.applyToChildren(new ReaderView.ViewMapper() { - void applyToView(View view) { - ((MuPDFView)view).releaseBitmaps(); - } - }); - } - if (core != null) - core.onDestroy(); - if (mAlertTask != null) { - mAlertTask.cancel(true); - mAlertTask = null; - } - core = null; - super.onDestroy(); - } - - private void setButtonEnabled(ImageButton button, boolean enabled) { - button.setEnabled(enabled); - button.setColorFilter(enabled ? Color.argb(255, 255, 255, 255) : Color - .argb(255, 128, 128, 128)); - } - /* - private void setLinkHighlight(boolean highlight) { - mLinkHighlight = highlight; - // LINK_COLOR tint - mLinkButton.setColorFilter(highlight ? Color.argb(0xFF, 172, 114, 37) - : Color.argb(0xFF, 255, 255, 255)); - // Inform pages of the change. - mDocView.setLinksEnabled(highlight); - } - */ - private void showButtons() { - if (core == null) - return; - if (!mButtonsVisible) { - mButtonsVisible = true; - // Update page number text and slider - int index = mDocView.getDisplayedViewIndex(); - updatePageNumView(index); - ///mPageSlider.setMax((core.countPages() - 1) * mPageSliderRes); - ///mPageSlider.setProgress(index * mPageSliderRes); - if (mTopBarMode == TopBarMode.Search) { - mSearchText.requestFocus(); - showKeyboard(); - } - - Animation anim = new TranslateAnimation(0, 0, - -mTopBarSwitcher.getHeight(), 0); - anim.setDuration(200); - anim.setAnimationListener(new Animation.AnimationListener() { - public void onAnimationStart(Animation animation) { - mTopBarSwitcher.setVisibility(View.VISIBLE); - } - - public void onAnimationRepeat(Animation animation) { - } - - public void onAnimationEnd(Animation animation) { - } - }); - mTopBarSwitcher.startAnimation(anim); - - // Update listView position - setCurrentlyViewedPreview(); - ///anim = new TranslateAnimation(0, 0, mPageSlider.getHeight(), 0); - anim = new TranslateAnimation(0, 0, mPreviewBarHolder.getHeight(), 0); - anim.setDuration(200); - anim.setAnimationListener(new Animation.AnimationListener() { - public void onAnimationStart(Animation animation) { - ///mPageSlider.setVisibility(View.VISIBLE); - mPreviewBarHolder.setVisibility(View.VISIBLE); - } - - public void onAnimationRepeat(Animation animation) { - } - - public void onAnimationEnd(Animation animation) { - mPageNumberView.setVisibility(View.VISIBLE); - } - }); - ///mPageSlider.startAnimation(anim); - mPreviewBarHolder.startAnimation(anim); - } - } - - private void hideButtons() { - if (mButtonsVisible) { - mButtonsVisible = false; - hideKeyboard(); - - Animation anim = new TranslateAnimation(0, 0, 0, - -mTopBarSwitcher.getHeight()); - anim.setDuration(200); - anim.setAnimationListener(new Animation.AnimationListener() { - public void onAnimationStart(Animation animation) { - } - - public void onAnimationRepeat(Animation animation) { - } - - public void onAnimationEnd(Animation animation) { - mTopBarSwitcher.setVisibility(View.INVISIBLE); - } - }); - mTopBarSwitcher.startAnimation(anim); - - ///anim = new TranslateAnimation(0, 0, 0, mPageSlider.getHeight()); - anim = new TranslateAnimation(0, 0, 0, mPreviewBarHolder.getHeight()); - anim.setDuration(200); - anim.setAnimationListener(new Animation.AnimationListener() { - public void onAnimationStart(Animation animation) { - mPageNumberView.setVisibility(View.INVISIBLE); - } - - public void onAnimationRepeat(Animation animation) { - } - - public void onAnimationEnd(Animation animation) { - ///mPageSlider.setVisibility(View.INVISIBLE); - mPreviewBarHolder.setVisibility(View.INVISIBLE); - } - }); - ///mPageSlider.startAnimation(anim); - mPreviewBarHolder.startAnimation(anim); - } - } - - private void searchModeOn() { - if (mTopBarMode != TopBarMode.Search) { - mTopBarMode = TopBarMode.Search; - // Focus on EditTextWidget - mSearchText.requestFocus(); - showKeyboard(); - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - } - - private void searchModeOff() { - if (mTopBarMode == TopBarMode.Search) { - mTopBarMode = TopBarMode.Main; - hideKeyboard(); - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - SearchTaskResult.set(null); - // Make the ReaderView act on the change to mSearchTaskResult - // via overridden onChildSetup method. - mDocView.resetupChildren(); - } - } - - @SuppressLint("DefaultLocale") - private void updatePageNumView(int index) { - if (core == null) - return; - String pageStr = ""; - try { - if (core.getDisplayPages() == 2 && index!=0 && index!=core.countPages()-1) { - pageStr = String.format("%1$d-%2$d", (index*2)-2, (index*2)-1); - mPageNumberView.setText(String.format(getString(TiRHelper.getResource("string.two_pages_of_count")), (index*2)-2, (index*2)-1, core.countSinglePages())); - } - else if (core.getDisplayPages() == 2 && (index==0 || index==core.countPages()-1)) { - pageStr = String.format("%1$d", index+1); - mPageNumberView.setText(String.format(getString(TiRHelper.getResource("string.one_page_of_count")), index+1, core.countSinglePages())); - } - else { - pageStr = String.format("%1$d", index+1); - mPageNumberView.setText(String.format(getString(TiRHelper.getResource("string.one_page_of_count")), index+1, core.countPages())); - } - } catch( ResourceNotFoundException e ){} - MuPDFCallbackClass.sendGaiView(String.format("documentView (%1$s), page (%2$s)", mDocName, pageStr)); - } - private void printDoc() { - if (!core.fileFormat().startsWith("PDF")) { - try { - showInfo(getString(TiRHelper.getResource("string.format_currently_not_supported"))); - } catch( ResourceNotFoundException e ){} - return; - } - - Intent myIntent = getIntent(); - Uri docUri = myIntent != null ? myIntent.getData() : null; - - if (docUri == null) { - try { - showInfo(getString(TiRHelper.getResource("string.print_failed"))); - } catch( ResourceNotFoundException e ){} - } - - if (docUri.getScheme() == null) - docUri = Uri.parse("file://" + docUri.toString()); - - Intent printIntent = new Intent(this, PrintDialogActivity.class); - printIntent.setDataAndType(docUri, "aplication/pdf"); - printIntent.putExtra("title", mFileName); - startActivityForResult(printIntent, PRINT_REQUEST); - } - - private void showInfo(String message) { - mInfoView.setText(message); - - int currentApiVersion = android.os.Build.VERSION.SDK_INT; - if (currentApiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { - try { - @SuppressWarnings("unused") - SafeAnimatorInflater safe = new SafeAnimatorInflater((Activity)this, TiRHelper.getResource("anim.info"), (View)mInfoView); - } catch( ResourceNotFoundException e ){} - } else { - mInfoView.setVisibility(View.VISIBLE); - mHandler.postDelayed(new Runnable() { - public void run() { - mInfoView.setVisibility(View.INVISIBLE); - } - }, 500); - } - } - - private void makeButtonsView() { - try { - mButtonsView = getLayoutInflater().inflate(TiRHelper.getResource("layout.buttons"), null); - // ///////mFilenameView = - // (TextView)mButtonsView.findViewById(R.id.docNameText); - ///mPageSlider = (SeekBar) mButtonsView.findViewById(R.id.pageSlider); - mPreviewBarHolder = (FrameLayout) mButtonsView.findViewById(TiRHelper.getResource("id.PreviewBarHolder")); - mPreview = new TwoWayView(this); - mPreview.setOrientation(Orientation.HORIZONTAL); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(-1, -1); - mPreview.setLayoutParams(lp); - pdfPreviewPagerAdapter = new ToolbarPreviewAdapter(this, core); - mPreview.setAdapter(pdfPreviewPagerAdapter); - mPreview.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView pArg0, View pArg1, - int position, long id) { - hideButtons(); - mDocView.setDisplayedViewIndex((int)id); - } - }); - } catch( ResourceNotFoundException e ){} - mPreviewBarHolder.addView(mPreview); - try { - mPageNumberView = (TextView) mButtonsView.findViewById(TiRHelper.getResource("id.pageNumber")); - mInfoView = (TextView) mButtonsView.findViewById(TiRHelper.getResource("id.info")); - mSearchButton = (ImageButton) mButtonsView.findViewById(TiRHelper.getResource("id.searchButton")); - //mReflowButton = (ImageButton) mButtonsView.findViewById(R.id.reflowButton); - //mOutlineButton = (ImageButton) mButtonsView.findViewById(R.id.outlineButton); - //mAnnotButton = (ImageButton)mButtonsView.findViewById(R.id.editAnnotButton); - //mAnnotTypeText = (TextView)mButtonsView.findViewById(R.id.annotType); - mTopBarSwitcher = (ViewAnimator) mButtonsView.findViewById(TiRHelper.getResource("id.switcher")); - mSearchBack = (ImageButton) mButtonsView.findViewById(TiRHelper.getResource("id.searchBack")); - mSearchFwd = (ImageButton) mButtonsView.findViewById(TiRHelper.getResource("id.searchForward")); - mSearchText = (EditText) mButtonsView.findViewById(TiRHelper.getResource("id.searchText")); - //mLinkButton = (ImageButton) mButtonsView.findViewById(R.id.linkButton); - //mMoreButton = (ImageButton) mButtonsView.findViewById(R.id.moreButton); - } catch( ResourceNotFoundException e ){} - mTopBarSwitcher.setVisibility(View.INVISIBLE); - mPageNumberView.setVisibility(View.INVISIBLE); - mInfoView.setVisibility(View.INVISIBLE); - ///mPageSlider.setVisibility(View.INVISIBLE); - mPreviewBarHolder.setVisibility(View.INVISIBLE); - } - - public void OnMoreButtonClick(View v) { - - - if (core != null) { - int i = mDocView.getDisplayedViewIndex(); - if (core.getDisplayPages() == 2) { - i = (i * 2) - 1; - } - PDFPreviewGridActivityData.get().core = core; - PDFPreviewGridActivityData.get().position = i; - //PDFPreviewGridActivity prevAct = new PDFPreviewGridActivity(); - //Intent intent = prevAct.getIntent(); - Intent intent = new Intent(MuPDFActivity.this, PDFPreviewGridActivity.class); - startActivityForResult(intent, PAGE_CHOICE_REQUEST); - - MuPDFCallbackClass.sendGaiView(String.format("documentThumbView (%1$s)", mDocName)); - - } - - /////////////mTopBarMode = TopBarMode.More; - /////////////mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - - public void OnCancelMoreButtonClick(View v) { - mTopBarMode = TopBarMode.Main; - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - - public void OnPrintButtonClick(View v) { - printDoc(); - } - - public void OnCloseReaderButtonClick(View v) { - finish(); - } -/* - public void OnCancelAcceptButtonClick(View v) { - MuPDFView pageView = (MuPDFView) mDocView.getDisplayedView(); - if (pageView != null) { - pageView.deselectText(); - pageView.cancelDraw(); - } - mDocView.setMode(MuPDFReaderView.Mode.Viewing); - - //switch (mAcceptMode) { - //case CopyText: - // mTopBarMode = TopBarMode.More; - // break; - //default: - //mTopBarMode = TopBarMode.Annot; - // break; - //} - - mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal()); - } - */ - - public void OnCancelSearchButtonClick(View v) { - searchModeOff(); - } - - private void showKeyboard() { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) - imm.showSoftInput(mSearchText, 0); - } - - private void hideKeyboard() { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) - imm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0); - } - - private void search(int direction) { - hideKeyboard(); - int displayPage = mDocView.getDisplayedViewIndex(); - SearchTaskResult r = SearchTaskResult.get(); - int searchPage = r != null ? r.pageNumber : -1; - mSearchTask.go(mSearchText.getText().toString(), direction, displayPage, searchPage); - } - - @Override - public boolean onSearchRequested() { - if (mButtonsVisible && mTopBarMode == TopBarMode.Search) { - hideButtons(); - } else { - showButtons(); - searchModeOn(); - } - return super.onSearchRequested(); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - if (mButtonsVisible && mTopBarMode != TopBarMode.Search) { - hideButtons(); - } else { - showButtons(); - searchModeOff(); - } - return super.onPrepareOptionsMenu(menu); - } - - @Override - protected void onStart() { - if (core != null) { - core.startAlerts(); - createAlertWaiter(); - } - - super.onStart(); - } - - @Override - protected void onStop() { - if (core != null) { - destroyAlertWaiter(); - core.stopAlerts(); - } - - super.onStop(); - } - - @Override - public void onBackPressed() { - //if (core.hasChanges()) { - if (core != null && core.hasChanges()) { - DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - if (which == AlertDialog.BUTTON_POSITIVE) - core.save(); - - finish(); - } - }; - AlertDialog alert = mAlertBuilder.create(); - alert.setTitle("MuPDF"); - try { - alert.setMessage(getString(TiRHelper.getResource("string.document_has_changes_save_them"))); - alert.setButton(AlertDialog.BUTTON_POSITIVE, - getString(TiRHelper.getResource("string.yes")), listener); - alert.setButton(AlertDialog.BUTTON_NEGATIVE, - getString(TiRHelper.getResource("string.no")), listener); - alert.show(); - } catch( ResourceNotFoundException e ){} - } else { - super.onBackPressed(); - } - } - - @Override - public void performPickFor(FilePicker picker) { - mFilePicker = picker; - //Intent intent = new Intent(this, ChoosePDFActivity.class); - //startActivityForResult(intent, FILEPICK_REQUEST); - } -} \ No newline at end of file diff --git a/android/src/com/artifex/mupdflib/MuPDFAlert.java b/android/src/com/artifex/mupdflib/MuPDFAlert.java index c9b6590..ca14270 100644 --- a/android/src/com/artifex/mupdflib/MuPDFAlert.java +++ b/android/src/com/artifex/mupdflib/MuPDFAlert.java @@ -1,17 +1,9 @@ package com.artifex.mupdflib; public class MuPDFAlert { - public enum IconType { - Error, Warning, Question, Status - }; - - public enum ButtonPressed { - None, Ok, Cancel, No, Yes - }; - - public enum ButtonGroupType { - Ok, OkCancel, YesNo, YesNoCancel - }; + public enum IconType {Error,Warning,Question,Status}; + public enum ButtonPressed {None,Ok,Cancel,No,Yes}; + public enum ButtonGroupType {Ok,OkCancel,YesNo,YesNoCancel}; public final String message; public final IconType iconType; @@ -19,9 +11,7 @@ public enum ButtonGroupType { public final String title; public ButtonPressed buttonPressed; - MuPDFAlert(String aMessage, IconType aIconType, - ButtonGroupType aButtonGroupType, String aTitle, - ButtonPressed aButtonPressed) { + MuPDFAlert(String aMessage, IconType aIconType, ButtonGroupType aButtonGroupType, String aTitle, ButtonPressed aButtonPressed) { message = aMessage; iconType = aIconType; buttonGroupType = aButtonGroupType; diff --git a/android/src/com/artifex/mupdflib/MuPDFAlertInternal.java b/android/src/com/artifex/mupdflib/MuPDFAlertInternal.java index df76889..94ff21d 100644 --- a/android/src/com/artifex/mupdflib/MuPDFAlertInternal.java +++ b/android/src/com/artifex/mupdflib/MuPDFAlertInternal.java @@ -8,8 +8,7 @@ public class MuPDFAlertInternal { public final String title; public int buttonPressed; - MuPDFAlertInternal(String aMessage, int aIconType, int aButtonGroupType, - String aTitle, int aButtonPressed) { + MuPDFAlertInternal(String aMessage, int aIconType, int aButtonGroupType, String aTitle, int aButtonPressed) { message = aMessage; iconType = aIconType; buttonGroupType = aButtonGroupType; @@ -26,8 +25,6 @@ public class MuPDFAlertInternal { } MuPDFAlert toAlert() { - return new MuPDFAlert(message, MuPDFAlert.IconType.values()[iconType], - MuPDFAlert.ButtonGroupType.values()[buttonGroupType], title, - MuPDFAlert.ButtonPressed.values()[buttonPressed]); + return new MuPDFAlert(message, MuPDFAlert.IconType.values()[iconType], MuPDFAlert.ButtonGroupType.values()[buttonGroupType], title, MuPDFAlert.ButtonPressed.values()[buttonPressed]); } } diff --git a/android/src/com/artifex/mupdflib/MuPDFCancellableTaskDefinition.java b/android/src/com/artifex/mupdflib/MuPDFCancellableTaskDefinition.java index 7d38102..c270d62 100644 --- a/android/src/com/artifex/mupdflib/MuPDFCancellableTaskDefinition.java +++ b/android/src/com/artifex/mupdflib/MuPDFCancellableTaskDefinition.java @@ -35,4 +35,4 @@ public final Result doInBackground(Params ... params) } public abstract Result doInBackground(MuPDFCore.Cookie cookie, Params ... params); -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/MuPDFCore.java b/android/src/com/artifex/mupdflib/MuPDFCore.java index d733de6..db0b21a 100644 --- a/android/src/com/artifex/mupdflib/MuPDFCore.java +++ b/android/src/com/artifex/mupdflib/MuPDFCore.java @@ -1,16 +1,16 @@ package com.artifex.mupdflib; -import android.content.Context; -import android.graphics.*; -import android.graphics.Bitmap.Config; -import android.util.Log; - -import java.io.File; import java.util.ArrayList; import org.appcelerator.titanium.util.TiRHelper; import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.PointF; +import android.graphics.RectF; + public class MuPDFCore { /* load our native library */ static { @@ -19,151 +19,178 @@ public class MuPDFCore { /* Readable members */ private int numPages = -1; - private int displayPages = 1; private float pageWidth; private float pageHeight; - private String mFileName; private long globals; private byte fileBuffer[]; private String file_format; private boolean isUnencryptedPDF; + private final boolean wasOpenedFromBuffer; /* The native functions */ private native long openFile(String filename); - //private native long openBuffer(); + private native long openBuffer(String magic); + private native String fileFormatInternal(); + private native boolean isUnencryptedPDFInternal(); + private native int countPagesInternal(); + private native void gotoPageInternal(int localActionPageNum); + private native float getPageWidth(); + private native float getPageHeight(); - private native void drawPage(Bitmap bitmap, int pageW, int pageH, int patchX, int patchY, int patchW, int patchH, long cookiePtr); - private native void updatePageInternal(Bitmap bitmap, int page, int pageW, int pageH, int patchX, int patchY, int patchW, int patchH, long cookiePtr); + + private native void drawPage(Bitmap bitmap, int pageW, int pageH, + int patchX, int patchY, int patchW, int patchH, long cookiePtr); + + private native void updatePageInternal(Bitmap bitmap, int page, int pageW, + int pageH, int patchX, int patchY, int patchW, int patchH, + long cookiePtr); + private native RectF[] searchPage(String text); + private native TextChar[][][][] text(); + private native byte[] textAsHtml(); - private native void addMarkupAnnotationInternal(PointF[] quadPoints, int type); + + private native void addMarkupAnnotationInternal(PointF[] quadPoints, + int type); + private native void addInkAnnotationInternal(PointF[][] arcs); + private native void deleteAnnotationInternal(int annot_index); + private native int passClickEventInternal(int page, float x, float y); + private native void setFocusedWidgetChoiceSelectedInternal(String[] selected); + private native String[] getFocusedWidgetChoiceSelected(); + private native String[] getFocusedWidgetChoiceOptions(); + private native int getFocusedWidgetSignatureState(); + private native String checkFocusedSignatureInternal(); - private native boolean signFocusedSignatureInternal(String keyFile, String password); + + private native boolean signFocusedSignatureInternal(String keyFile, + String password); + private native int setFocusedWidgetTextInternal(String text); + private native String getFocusedWidgetTextInternal(); + private native int getFocusedWidgetTypeInternal(); + private native LinkInfo[] getPageLinksInternal(int page); + private native RectF[] getWidgetAreasInternal(int page); + private native Annotation[] getAnnotationsInternal(int page); + private native OutlineItem[] getOutlineInternal(); + private native boolean hasOutlineInternal(); + private native boolean needsPasswordInternal(); + private native boolean authenticatePasswordInternal(String password); + private native MuPDFAlertInternal waitForAlertInternal(); + private native void replyToAlertInternal(MuPDFAlertInternal alert); + private native void startAlertsInternal(); + private native void stopAlertsInternal(); + private native void destroying(); + private native boolean hasChangesInternal(); + private native void saveInternal(); + private native long createCookie(); + private native void destroyCookie(long cookie); + private native void abortCookie(long cookie); - public native boolean javascriptSupported(); - - public class Cookie - { + + public class Cookie { private final long cookiePtr; - public Cookie() - { + public Cookie() { cookiePtr = createCookie(); if (cookiePtr == 0) throw new OutOfMemoryError(); } - public void abort() - { + public void abort() { abortCookie(cookiePtr); } - public void destroy() - { + public void destroy() { // We could do this in finalize, but there's no guarantee that // a finalize will occur before the muPDF context occurs. destroyCookie(cookiePtr); } } - - public enum WidgetType { - NONE, TEXT, LISTBOX, COMBOBOX, SIGNATURE - } - public MuPDFCore(Context context, String filename) throws Exception { - mFileName = filename; globals = openFile(filename); if (globals == 0) { try { - throw new Exception(String.format(context.getString(TiRHelper.getResource("string.cannot_open_file_Path")), filename)); - } catch( ResourceNotFoundException e ){} + throw new Exception( + String.format(context.getString(TiRHelper + .getResource("string.cannot_open_file_Path")), + filename)); + } catch (ResourceNotFoundException e) { + } } file_format = fileFormatInternal(); isUnencryptedPDF = isUnencryptedPDFInternal(); + wasOpenedFromBuffer = false; } - public String getFileName() { - return mFileName; - } - - public String getFileDirectory() { - return (new File(getFileName())).getParent(); - } - - //public MuPDFCore(Context context, byte buffer[]) throws Exception { - public MuPDFCore(Context context, byte buffer[], String magic) throws Exception { - + public MuPDFCore(Context context, byte buffer[], String magic) + throws Exception { fileBuffer = buffer; - //globals = openBuffer(); globals = openBuffer(magic != null ? magic : ""); - if (globals == 0) { try { - throw new Exception(context.getString(TiRHelper.getResource("string.cannot_open_buffer"))); - } catch( ResourceNotFoundException e ){} + throw new Exception(context.getString(TiRHelper + .getResource("string.cannot_open_buffer"))); + } catch (ResourceNotFoundException e) { + } } file_format = fileFormatInternal(); isUnencryptedPDF = isUnencryptedPDFInternal(); + wasOpenedFromBuffer = true; } public int countPages() { if (numPages < 0) numPages = countPagesSynchronized(); - - if(displayPages == 1) - return numPages; - if(numPages % 2 == 0) { - return numPages / 2 + 1; - } - int toReturn = numPages / 2; - return toReturn + 1; + return numPages; } public String fileFormat() { return file_format; } - - public boolean isUnencryptedPDF() - { + + public boolean isUnencryptedPDF() { return isUnencryptedPDF; } + public boolean wasOpenedFromBuffer() { + return wasOpenedFromBuffer; + } + private synchronized int countPagesSynchronized() { return countPagesInternal(); } @@ -180,22 +207,8 @@ else if (page < 0) } public synchronized PointF getPageSize(int page) { - if (displayPages == 1) { - gotoPage(page); - return new PointF(pageWidth, pageHeight); - } else { - gotoPage(page); - if (page == numPages - 1 || page == 0) { - // last page - return new PointF(pageWidth * 2, pageHeight); - } - float leftWidth = pageWidth; - float leftHeight = pageHeight; - gotoPage(page + 1); - float screenWidth = leftWidth + pageWidth; - float screenHeight = Math.max(leftHeight, pageHeight); - return new PointF(screenWidth, screenHeight); - } + gotoPage(page); + return new PointF(pageWidth, pageHeight); } public MuPDFAlert waitForAlert() { @@ -219,214 +232,20 @@ public synchronized void onDestroy() { destroying(); globals = 0; } - - public synchronized PointF getSinglePageSize(int page) { - gotoPage(page); - return new PointF(pageWidth, pageHeight); - } - public synchronized void drawPageSynchrinized(int page, Bitmap bitmap, int pageW, - int pageH, int patchX, int patchY, int patchW, int patchH) { + public synchronized void drawPage(Bitmap bm, int page, int pageW, + int pageH, int patchX, int patchY, int patchW, int patchH, + MuPDFCore.Cookie cookie) { gotoPage(page); - ///Log.d(TAG,"drawPageSynchrinized page:"+page); - drawPage(bitmap, pageW, pageH, patchX, patchY, patchW, patchH, 0); - } - - public synchronized void drawSinglePage(int page, Bitmap bitmap, int pageW, - int pageH) { - - drawPageSynchrinized(page, bitmap, pageW, pageH, 0, 0, pageW, pageH); - } - - public synchronized void drawPage(Bitmap bm, int page, int pageW, int pageH, - int patchX, int patchY, int patchW, int patchH, MuPDFCore.Cookie cookie) { - ///gotoPage(page); - ///Bitmap bm = Bitmap.createBitmap(patchW, patchH, Config.ARGB_8888); - ///drawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH); - ///return bm; - Canvas canvas = null; - //Bitmap bitmap = null; - try { - //bitmap = Bitmap.createBitmap(patchW, patchH, Config.ARGB_8888); - canvas = new Canvas(bm); - canvas.drawColor(Color.TRANSPARENT); - ///Log.d(TAG, "drawPage "+page); - - ///Log.d(TAG,"canvas: "+canvas); - if (displayPages == 1) { - gotoPage(page); - drawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr); - //return bitmap; - } else { - final int drawPage = (page == 0) ? 0 : page * 2 - 1; - int leftPageW = pageW / 2; - int rightPageW = pageW - leftPageW; - - // If patch overlaps both bitmaps (left and right) - return the - // width of overlapping left bitpam part of the patch - // or return full patch width if it's fully inside left bitmap - int leftBmWidth = Math.min(leftPageW, leftPageW - patchX); - - // set left Bitmap width to zero if patch is fully overlay right - // Bitmap - leftBmWidth = (leftBmWidth < 0) ? 0 : leftBmWidth; - - // set the right part of the patch width, as a rest of the patch - int rightBmWidth = patchW - leftBmWidth; - - if (drawPage == numPages - 1) { - // draw only left page - canvas.drawColor(Color.BLACK); - if (leftBmWidth > 0) { - Bitmap leftBm = Bitmap.createBitmap(leftBmWidth, patchH, - getBitmapConfig()); - gotoPage(drawPage); - drawPage(leftBm, leftPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, leftBmWidth, patchH, cookie.cookiePtr); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - canvas.drawBitmap(leftBm, 0, 0, paint); - leftBm.recycle(); - } - } else if (drawPage == 0) { - // draw only right page - canvas.drawColor(Color.BLACK); - if (rightBmWidth > 0) { - Bitmap rightBm = Bitmap.createBitmap(rightBmWidth, patchH, - getBitmapConfig()); - gotoPage(drawPage); - drawPage(rightBm, rightPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, rightBmWidth, patchH, cookie.cookiePtr); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - canvas.drawBitmap(rightBm, leftBmWidth, 0, paint); - rightBm.recycle(); - } - } else { - // Need to draw two pages one by one: left and right - Log.d("bitmap width", "" + bm.getWidth()); -// canvas.drawColor(Color.BLACK); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - if (leftBmWidth > 0) { - Bitmap leftBm = Bitmap.createBitmap(leftBmWidth, - patchH, getBitmapConfig()); - gotoPage(drawPage); - drawPage(leftBm, leftPageW, pageH, patchX, patchY, - leftBmWidth, patchH, cookie.cookiePtr); - canvas.drawBitmap(leftBm, 0, 0, paint); - leftBm.recycle(); - } - if (rightBmWidth > 0) { - Bitmap rightBm = Bitmap.createBitmap(rightBmWidth, - patchH, getBitmapConfig()); - gotoPage(drawPage+1); - drawPage(rightBm, rightPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, rightBmWidth, patchH, cookie.cookiePtr); - - canvas.drawBitmap(rightBm, (float) leftBmWidth, 0, - paint); - rightBm.recycle(); - } - - } - //return bitmap; - } - } catch (OutOfMemoryError e) { - ///Log.e(TAG, "draw page " + page + "failed", e); - if(canvas != null) canvas.drawColor(Color.TRANSPARENT); - //return bitmap; - } + drawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH, + cookie.cookiePtr); } public synchronized void updatePage(Bitmap bm, int page, int pageW, - int pageH, int patchX, int patchY, int patchW, int patchH, MuPDFCore.Cookie cookie) { - - Canvas canvas = null; - - try { - canvas = new Canvas(bm); - canvas.drawColor(Color.TRANSPARENT); - ///Log.d(TAG,"canvas: "+canvas); - if (displayPages == 1) { - - updatePageInternal(bm, page, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr); - //return bitmap; - } else { - page = (page == 0) ? 0 : page * 2 - 1; - int leftPageW = pageW / 2; - int rightPageW = pageW - leftPageW; - - // If patch overlaps both bitmaps (left and right) - return the - // width of overlapping left bitpam part of the patch - // or return full patch width if it's fully inside left bitmap - int leftBmWidth = Math.min(leftPageW, leftPageW - patchX); - - // set left Bitmap width to zero if patch is fully overlay right - // Bitmap - leftBmWidth = (leftBmWidth < 0) ? 0 : leftBmWidth; - - // set the right part of the patch width, as a rest of the patch - int rightBmWidth = patchW - leftBmWidth; - - if (page == numPages - 1) { - // draw only left page -// canvas.drawColor(Color.BLACK); - if (leftBmWidth > 0) { - Bitmap leftBm = Bitmap.createBitmap(bm, 0, 0, leftBmWidth, patchH); - updatePageInternal(leftBm, page, leftPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, leftBmWidth, patchH, cookie.cookiePtr); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - canvas.drawBitmap(leftBm, 0, 0, paint); - leftBm.recycle(); - } - } else if (page == 0) { - // draw only right page -// canvas.drawColor(Color.BLACK); - if (rightBmWidth > 0) { - Bitmap rightBm = Bitmap.createBitmap(bm, leftBmWidth, 0, rightBmWidth, patchH); - gotoPage(page); - updatePageInternal(rightBm, page, rightPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, rightBmWidth, patchH, cookie.cookiePtr); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - canvas.drawBitmap(rightBm, leftBmWidth, 0, paint); - rightBm.recycle(); - } - } else { - // Need to draw two pages one by one: left and right - Log.d("bitmap width", "" + bm.getWidth()); -// canvas.drawColor(Color.BLACK); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); - if (leftBmWidth > 0) { - Bitmap leftBm = Bitmap.createBitmap(bm, 0, 0, (leftBmWidth < bm.getWidth()) ? leftBmWidth : bm.getWidth(), - patchH); - updatePageInternal(leftBm, page, leftPageW, pageH, patchX, patchY, - leftBmWidth, patchH, cookie.cookiePtr); - canvas.drawBitmap(leftBm, 0, 0, paint); - leftBm.recycle(); - } - if (rightBmWidth > 0) { - Bitmap rightBm = Bitmap.createBitmap(bm, leftBmWidth, 0, rightBmWidth, - patchH); - updatePageInternal(rightBm, page, rightPageW, pageH, - (leftBmWidth == 0) ? patchX - leftPageW : 0, - patchY, rightBmWidth, patchH, cookie.cookiePtr); - - canvas.drawBitmap(rightBm, (float) leftBmWidth, 0, - paint); - rightBm.recycle(); - } - - } - //return bitmap; - } - } catch (OutOfMemoryError e) { - ///Log.e(TAG, "update page " + page + "failed", e); - if(canvas != null) canvas.drawColor(Color.TRANSPARENT); - //return bitmap; - } + int pageH, int patchX, int patchY, int patchW, int patchH, + MuPDFCore.Cookie cookie) { + updatePageInternal(bm, page, pageW, pageH, patchX, patchY, patchW, + patchH, cookie.cookiePtr); } public synchronized PassClickResult passClickEvent(int page, float x, @@ -435,12 +254,16 @@ public synchronized PassClickResult passClickEvent(int page, float x, switch (WidgetType.values()[getFocusedWidgetTypeInternal()]) { case TEXT: - return new PassClickResultText(changed, getFocusedWidgetTextInternal()); + return new PassClickResultText(changed, + getFocusedWidgetTextInternal()); case LISTBOX: case COMBOBOX: - return new PassClickResultChoice(changed, getFocusedWidgetChoiceOptions(), getFocusedWidgetChoiceSelected()); + return new PassClickResultChoice(changed, + getFocusedWidgetChoiceOptions(), + getFocusedWidgetChoiceSelected()); case SIGNATURE: - return new PassClickResultSignature(changed, getFocusedWidgetSignatureState()); + return new PassClickResultSignature(changed, + getFocusedWidgetSignatureState()); default: return new PassClickResult(changed); } @@ -458,55 +281,18 @@ public synchronized boolean setFocusedWidgetText(int page, String text) { public synchronized void setFocusedWidgetChoiceSelected(String[] selected) { setFocusedWidgetChoiceSelectedInternal(selected); } - + public synchronized String checkFocusedSignature() { return checkFocusedSignatureInternal(); } - - public synchronized boolean signFocusedSignature(String keyFile, String password) { + + public synchronized boolean signFocusedSignature(String keyFile, + String password) { return signFocusedSignatureInternal(keyFile, password); } public synchronized LinkInfo[] getPageLinks(int page) { - ///return getPageLinksInternal(page); - if(displayPages == 1) - return getPageLinksInternal(page); - LinkInfo[] leftPageLinkInfo = new LinkInfo[0]; - LinkInfo[] rightPageLinkInfo = new LinkInfo[0]; - LinkInfo[] combinedLinkInfo; - int combinedSize = 0; - int rightPage = page * 2; - int leftPage = rightPage - 1; - int count = countPages() * 2; - if( leftPage > 0 ) { - LinkInfo[] leftPageLinkInfoInternal = getPageLinksInternal(leftPage); - if (null != leftPageLinkInfoInternal) { - leftPageLinkInfo = leftPageLinkInfoInternal; - combinedSize += leftPageLinkInfo.length; - } - } - if( rightPage < count ) { - LinkInfo[] rightPageLinkInfoInternal = getPageLinksInternal(rightPage); - if (null != rightPageLinkInfoInternal) { - rightPageLinkInfo = rightPageLinkInfoInternal; - combinedSize += rightPageLinkInfo.length; - } - } - - combinedLinkInfo = new LinkInfo[combinedSize]; - for(int i = 0; i < leftPageLinkInfo.length; i++) { - combinedLinkInfo[i] = leftPageLinkInfo[i]; - } - - LinkInfo temp; - for(int i = 0, j = leftPageLinkInfo.length; i < rightPageLinkInfo.length; i++, j++) { - temp = rightPageLinkInfo[i]; - temp.rect.left += pageWidth; - temp.rect.right += pageWidth; - combinedLinkInfo[j] = temp; - } - - return combinedLinkInfo; + return getPageLinksInternal(page); } public synchronized RectF[] getWidgetAreas(int page) { @@ -604,33 +390,4 @@ public synchronized boolean hasChanges() { public synchronized void save() { saveInternal(); } - - public int getDisplayPages() { - return displayPages; - } - - private Config getBitmapConfig(){ - return Config.ARGB_8888; - } - - public int countDisplays() { - int pages = countPages(); - if(pages % 2 == 0) { - return pages / 2 + 1; - } else - return pages / 2; - } - - public void setDisplayPages(int pages) throws IllegalStateException { - if(pages <=0 || pages > 2) { - throw new IllegalStateException("MuPDFCore can only handle 1 or 2 pages per screen!"); - } - displayPages = pages; - } - /** - * @return - */ - public int countSinglePages() { - return numPages; - } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/MuPDFPageAdapter.java b/android/src/com/artifex/mupdflib/MuPDFPageAdapter.java index 17ac93d..a1e5c67 100644 --- a/android/src/com/artifex/mupdflib/MuPDFPageAdapter.java +++ b/android/src/com/artifex/mupdflib/MuPDFPageAdapter.java @@ -17,7 +17,6 @@ public class MuPDFPageAdapter extends BaseAdapter { private Bitmap mSharedHqBm; public MuPDFPageAdapter(Context c, FilePicker.FilePickerSupport filePickerSupport, MuPDFCore core) { - mContext = c; mFilePickerSupport = filePickerSupport; mCore = core; @@ -55,7 +54,7 @@ public View getView(final int position, View convertView, ViewGroup parent) { // Page size as yet unknown. Blank it for now, and // start a background task to find the size pageView.blank(position); - AsyncTask sizingTask = new AsyncTask() { + AsyncTask sizingTask = new AsyncTask() { @Override protected PointF doInBackground(Void... arg0) { return mCore.getPageSize(position); @@ -73,7 +72,7 @@ protected void onPostExecute(PointF result) { } }; - sizingTask.execute((Void) null); + sizingTask.execute((Void)null); } return pageView; } diff --git a/android/src/com/artifex/mupdflib/MuPDFPageView.java b/android/src/com/artifex/mupdflib/MuPDFPageView.java index 29074af..5db8731 100644 --- a/android/src/com/artifex/mupdflib/MuPDFPageView.java +++ b/android/src/com/artifex/mupdflib/MuPDFPageView.java @@ -1,5 +1,12 @@ package com.artifex.mupdflib; +import java.util.ArrayList; + +import org.appcelerator.titanium.util.TiRHelper; +import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; + +import com.artifex.mupdflib.MuPDFCore.Cookie; + import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.ClipData; @@ -17,21 +24,16 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; -import java.util.ArrayList; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - /* This enum should be kept in line with the cooresponding C enum in mupdf.c */ enum SignatureState { - NoSupport, - Unsigned, - Signed + NoSupport, Unsigned, Signed } abstract class PassClickResultVisitor { public abstract void visitText(PassClickResultText result); + public abstract void visitChoice(PassClickResultChoice result); + public abstract void visitSignature(PassClickResultSignature result); } @@ -111,134 +113,159 @@ public class MuPDFPageView extends PageView implements MuPDFView { private AsyncTask mAddStrikeOut; private AsyncTask mAddInk; private AsyncTask mDeleteAnnotation; - private AsyncTask mCheckSignature; - private AsyncTask mSign; + private AsyncTask mCheckSignature; + private AsyncTask mSign; private Runnable changeReporter; - public MuPDFPageView(Context c, FilePicker.FilePickerSupport filePickerSupport, MuPDFCore core, Point parentSize, Bitmap sharedHqBm) { + public MuPDFPageView(Context c, + FilePicker.FilePickerSupport filePickerSupport, MuPDFCore core, + Point parentSize, Bitmap sharedHqBm) { super(c, parentSize, sharedHqBm); mFilePickerSupport = filePickerSupport; mCore = core; try { - mTextEntryBuilder = new AlertDialog.Builder(c); - mTextEntryBuilder.setTitle("MuPDF: " - + getContext().getString(TiRHelper.getResource("string.fill_out_text_field"))); - LayoutInflater inflater = (LayoutInflater) c - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mEditText = (EditText) inflater.inflate(TiRHelper.getResource("layout.textentry"), null); - mTextEntryBuilder.setView(mEditText); - mTextEntryBuilder.setNegativeButton(TiRHelper.getResource("string.cancel"), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - mTextEntryBuilder.setPositiveButton(TiRHelper.getResource("string.okay"), - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - mSetWidgetText = new AsyncTask() { - @Override - protected Boolean doInBackground(String... arg0) { - return mCore.setFocusedWidgetText(mPageNumber, - arg0[0]); - } - - @Override - protected void onPostExecute(Boolean result) { - changeReporter.run(); - if (!result) - invokeTextDialog(mEditText.getText() - .toString()); - } - }; - - mSetWidgetText.execute(mEditText.getText().toString()); - } - }); - - mTextEntry = mTextEntryBuilder.create(); + mTextEntryBuilder = new AlertDialog.Builder(c); + mTextEntryBuilder + .setTitle("MuPDF: " + + getContext() + .getString( + TiRHelper + .getResource("string.fill_out_text_field"))); + LayoutInflater inflater = (LayoutInflater) c + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mEditText = (EditText) inflater.inflate( + TiRHelper.getResource("layout.textentry"), null); + mTextEntryBuilder.setView(mEditText); + mTextEntryBuilder.setNegativeButton( + TiRHelper.getResource("string.cancel"), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + mTextEntryBuilder.setPositiveButton( + TiRHelper.getResource("string.okay"), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mSetWidgetText = new AsyncTask() { + @Override + protected Boolean doInBackground(String... arg0) { + return mCore.setFocusedWidgetText( + mPageNumber, arg0[0]); + } + + @Override + protected void onPostExecute(Boolean result) { + changeReporter.run(); + if (!result) + invokeTextDialog(mEditText.getText() + .toString()); + } + }; + + mSetWidgetText.execute(mEditText.getText() + .toString()); + } + }); - mChoiceEntryBuilder = new AlertDialog.Builder(c); - mChoiceEntryBuilder.setTitle("MuPDF: " + getContext().getString(TiRHelper.getResource("string.choose_value"))); + mTextEntry = mTextEntryBuilder.create(); - mSigningDialogBuilder = new AlertDialog.Builder(c); - mSigningDialogBuilder.setTitle("Select certificate and sign?"); - mSigningDialogBuilder.setNegativeButton(TiRHelper.getResource("string.cancel"), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } + mChoiceEntryBuilder = new AlertDialog.Builder(c); + mChoiceEntryBuilder.setTitle("MuPDF: " + + getContext().getString( + TiRHelper.getResource("string.choose_value"))); - }); - mSigningDialogBuilder.setPositiveButton(TiRHelper.getResource("string.okay"), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - FilePicker picker = new FilePicker(mFilePickerSupport) { - @Override - void onPick(Uri uri) { - signWithKeyFile(uri); - } - }; + mSigningDialogBuilder = new AlertDialog.Builder(c); + mSigningDialogBuilder.setTitle("Select certificate and sign?"); + mSigningDialogBuilder.setNegativeButton( + TiRHelper.getResource("string.cancel"), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } - picker.pick(); - } + }); + mSigningDialogBuilder.setPositiveButton( + TiRHelper.getResource("string.okay"), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + FilePicker picker = new FilePicker( + mFilePickerSupport) { + @Override + void onPick(Uri uri) { + signWithKeyFile(uri); + } + }; + + picker.pick(); + } - }); - - mSignatureReportBuilder = new AlertDialog.Builder(c); - mSignatureReportBuilder.setTitle("Signature checked"); - mSignatureReportBuilder.setPositiveButton(TiRHelper.getResource("string.okay"), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); + }); - mPasswordText = new EditText(c); - mPasswordText.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); - mPasswordText.setTransformationMethod(new PasswordTransformationMethod()); + mSignatureReportBuilder = new AlertDialog.Builder(c); + mSignatureReportBuilder.setTitle("Signature checked"); + mSignatureReportBuilder.setPositiveButton( + TiRHelper.getResource("string.okay"), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); - mPasswordEntryBuilder = new AlertDialog.Builder(c); - mPasswordEntryBuilder.setTitle(TiRHelper.getResource("string.enter_password")); - mPasswordEntryBuilder.setView(mPasswordText); - mPasswordEntryBuilder.setNegativeButton(TiRHelper.getResource("string.cancel"), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); + mPasswordText = new EditText(c); + mPasswordText.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + mPasswordText + .setTransformationMethod(new PasswordTransformationMethod()); + + mPasswordEntryBuilder = new AlertDialog.Builder(c); + mPasswordEntryBuilder.setTitle(TiRHelper + .getResource("string.enter_password")); + mPasswordEntryBuilder.setView(mPasswordText); + mPasswordEntryBuilder.setNegativeButton( + TiRHelper.getResource("string.cancel"), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); } catch (ResourceNotFoundException exp) { } mPasswordEntry = mPasswordEntryBuilder.create(); } private void signWithKeyFile(final Uri uri) { - mPasswordEntry.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); - mPasswordEntry.setButton(AlertDialog.BUTTON_POSITIVE, "Sign", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - signWithKeyFileAndPassword(uri, mPasswordText.getText().toString()); - } - }); + mPasswordEntry.getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + mPasswordEntry.setButton(AlertDialog.BUTTON_POSITIVE, "Sign", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + signWithKeyFileAndPassword(uri, mPasswordText.getText() + .toString()); + } + }); mPasswordEntry.show(); } private void signWithKeyFileAndPassword(final Uri uri, final String password) { - mSign = new AsyncTask() { + mSign = new AsyncTask() { @Override protected Boolean doInBackground(Void... params) { - return mCore.signFocusedSignature(Uri.decode(uri.getEncodedPath()), password); + return mCore.signFocusedSignature( + Uri.decode(uri.getEncodedPath()), password); } + @Override protected void onPostExecute(Boolean result) { - if (result) - { + if (result) { changeReporter.run(); - } - else - { + } else { mPasswordText.setText(""); signWithKeyFile(uri); } @@ -247,7 +274,6 @@ protected void onPostExecute(Boolean result) { }; mSign.execute(); - } public LinkInfo hitLink(float x, float y) { @@ -258,13 +284,11 @@ public LinkInfo hitLink(float x, float y) { float scale = mSourceScale * (float) getWidth() / (float) mSize.x; float docRelX = (x - getLeft()) / scale; float docRelY = (y - getTop()) / scale; - - if (mLinks!=null) { - for (LinkInfo l : mLinks) - if (l.rect.contains(docRelX, docRelY)) - return l; - } - + + for (LinkInfo l : mLinks) + if (l.rect.contains(docRelX, docRelY)) + return l; + return null; } @@ -299,12 +323,14 @@ protected void onPostExecute(Void result) { AlertDialog dialog = mChoiceEntryBuilder.create(); dialog.show(); } + private void invokeSignatureCheckingDialog() { - mCheckSignature = new AsyncTask () { + mCheckSignature = new AsyncTask() { @Override protected String doInBackground(Void... params) { return mCore.checkFocusedSignature(); } + @Override protected void onPostExecute(String result) { AlertDialog report = mSignatureReportBuilder.create(); @@ -320,13 +346,13 @@ private void invokeSigningDialog() { AlertDialog dialog = mSigningDialogBuilder.create(); dialog.show(); } - + private void warnNoSignatureSupport() { AlertDialog dialog = mSignatureReportBuilder.create(); dialog.setTitle("App built with no signature support"); dialog.show(); } - + public void setChangeReporter(Runnable reporter) { changeReporter = reporter; } @@ -396,11 +422,8 @@ public void visitChoice(PassClickResultChoice result) { } @Override - public void visitSignature(PassClickResultSignature result) { - //if (result.isSigned) - // invokeSignatureCheckingDialog(); - //else - // invokeSigningDialog(); + public void visitSignature( + PassClickResultSignature result) { switch (result.state) { case NoSupport: warnNoSignatureSupport(); @@ -424,7 +447,6 @@ public void visitSignature(PassClickResultSignature result) { return Hit.Nothing; } - @SuppressWarnings("deprecation") @TargetApi(11) public boolean copySelection() { final StringBuilder text = new StringBuilder(); @@ -579,60 +601,49 @@ protected void onPostExecute(Void result) { return true; } - - /* - protected void drawPage(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, - int patchWidth, int patchHeight) { - mCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, - patchWidth, patchHeight); - } - */ @Override - protected CancellableTaskDefinition getDrawPageTask(final Bitmap bm, final int sizeX, final int sizeY, - final int patchX, final int patchY, final int patchWidth, final int patchHeight) { + protected CancellableTaskDefinition getDrawPageTask( + final Bitmap bm, final int sizeX, final int sizeY, + final int patchX, final int patchY, final int patchWidth, + final int patchHeight) { return new MuPDFCancellableTaskDefinition(mCore) { @Override - public Void doInBackground(MuPDFCore.Cookie cookie, Void ... params) { - // Workaround bug in Android Honeycomb 3.x, where the bitmap generation count + public Void doInBackground(MuPDFCore.Cookie cookie, Void... params) { + // Workaround bug in Android Honeycomb 3.x, where the bitmap + // generation count // is not incremented when drawing. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && - Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB + && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) bm.eraseColor(0); - mCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie); + mCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, + patchWidth, patchHeight, cookie); return null; } }; } - - /* - protected void updatePage(Bitmap bm, int sizeX, int sizeY, - int patchX, int patchY, int patchWidth, int patchHeight) { - mCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, - patchWidth, patchHeight); - } - */ - @Override - protected CancellableTaskDefinition getUpdatePageTask(final Bitmap bm, final int sizeX, final int sizeY, - final int patchX, final int patchY, final int patchWidth, final int patchHeight) - { + protected CancellableTaskDefinition getUpdatePageTask( + final Bitmap bm, final int sizeX, final int sizeY, + final int patchX, final int patchY, final int patchWidth, + final int patchHeight) { return new MuPDFCancellableTaskDefinition(mCore) { @Override - public Void doInBackground(MuPDFCore.Cookie cookie, Void ... params) { - // Workaround bug in Android Honeycomb 3.x, where the bitmap generation count + public Void doInBackground(MuPDFCore.Cookie cookie, Void... params) { + // Workaround bug in Android Honeycomb 3.x, where the bitmap + // generation count // is not incremented when drawing. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && - Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB + && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) bm.eraseColor(0); - mCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie); + mCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, + patchWidth, patchHeight, cookie); return null; } }; } - @Override protected LinkInfo[] getLinkInfo() { return mCore.getPageLinks(mPageNumber); @@ -732,4 +743,4 @@ public void releaseResources() { super.releaseResources(); } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/MuPDFReaderView.java b/android/src/com/artifex/mupdflib/MuPDFReaderView.java index 82f492c..ecdf5fe 100644 --- a/android/src/com/artifex/mupdflib/MuPDFReaderView.java +++ b/android/src/com/artifex/mupdflib/MuPDFReaderView.java @@ -1,5 +1,6 @@ package com.artifex.mupdflib; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -11,12 +12,12 @@ import android.view.WindowManager; public class MuPDFReaderView extends ReaderView { - enum Mode { + public enum Mode { Viewing, Selecting, Drawing } private final Context mContext; - private boolean mLinksHighlighted = false; + private boolean mLinksEnabled = false; private Mode mMode = Mode.Viewing; private boolean tapDisabled = false; private int tapPageMargin; @@ -30,21 +31,16 @@ protected void onDocMotion() { protected void onHit(Hit item) { }; - public void setLinksHighlighted(boolean b) { - mLinksHighlighted = b; + public void setLinksEnabled(boolean b) { + mLinksEnabled = b; resetupChildren(); } public void setMode(Mode m) { mMode = m; } - - private void setup() { - //public MuPDFReaderView(Activity act) { - //super(act); - //mContext = act; - + private void setup() { // Get the screen size etc to customise tap margins. // We calculate the size of 1 inch of the screen for tapping. // On some devices the dpi values returned are wrong, so we @@ -53,25 +49,23 @@ private void setup() { // dimension I've seen is 480 pixels or so). Then we check // to ensure we are never more than 1/5 of the screen width. DisplayMetrics dm = new DisplayMetrics(); - //act.getWindowManager().getDefaultDisplay().getMetrics(dm); - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + WindowManager wm = (WindowManager) mContext + .getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getMetrics(dm); - tapPageMargin = (int) dm.xdpi; - if (tapPageMargin < 80) - tapPageMargin = 80; - if (tapPageMargin > dm.widthPixels / 6) - tapPageMargin = dm.widthPixels / 6; + if (tapPageMargin < 100) + tapPageMargin = 100; + if (tapPageMargin > dm.widthPixels / 5) + tapPageMargin = dm.widthPixels / 5; } - + public MuPDFReaderView(Context context) { super(context); mContext = context; setup(); } - public MuPDFReaderView(Context context, AttributeSet attrs) - { + public MuPDFReaderView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; setup(); @@ -85,18 +79,20 @@ public boolean onSingleTapUp(MotionEvent e) { Hit item = pageView.passClickEvent(e.getX(), e.getY()); onHit(item); if (item == Hit.Nothing) { - if (/*mLinksEnabled && */pageView != null && (link = pageView.hitLink(e.getX(), e.getY())) != null) { + if (mLinksEnabled + && pageView != null + && (link = pageView.hitLink(e.getX(), e.getY())) != null) { link.acceptVisitor(new LinkInfoVisitor() { @Override public void visitInternal(LinkInfoInternal li) { // Clicked on an internal (GoTo) link - //TODO: goto page in landscape mode setDisplayedViewIndex(li.pageNumber); } @Override public void visitExternal(LinkInfoExternal li) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(li.url)); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri + .parse(li.url)); mContext.startActivity(intent); } @@ -109,10 +105,10 @@ public void visitRemote(LinkInfoRemote li) { super.smartMoveBackwards(); } else if (e.getX() > super.getWidth() - tapPageMargin) { super.smartMoveForwards(); - //} else if (e.getY() < tapPageMargin) { - // super.smartMoveBackwards(); - //} else if (e.getY() > super.getHeight() - tapPageMargin) { - // super.smartMoveForwards(); + } else if (e.getY() < tapPageMargin) { + super.smartMoveBackwards(); + } else if (e.getY() > super.getHeight() - tapPageMargin) { + super.smartMoveForwards(); } else { onTapMainDocArea(); } @@ -123,11 +119,12 @@ public void visitRemote(LinkInfoRemote li) { @Override public boolean onDown(MotionEvent e) { - + return super.onDown(e); } - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { MuPDFView pageView = (MuPDFView) getDisplayedView(); switch (mMode) { case Viewing: @@ -145,7 +142,8 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d } @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { switch (mMode) { case Viewing: return super.onFling(e1, e2, velocityX, velocityY); @@ -164,41 +162,37 @@ public boolean onScaleBegin(ScaleGestureDetector d) { public boolean onTouchEvent(MotionEvent event) { - if ( mMode == Mode.Drawing ) - { + if (mMode == Mode.Drawing) { float x = event.getX(); float y = event.getY(); - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - touch_start(x, y); - break; - case MotionEvent.ACTION_MOVE: - touch_move(x, y); - break; - case MotionEvent.ACTION_UP: - touch_up(); - break; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touch_start(x, y); + break; + case MotionEvent.ACTION_MOVE: + touch_move(x, y); + break; + case MotionEvent.ACTION_UP: + touch_up(); + break; } } - if ((event.getAction() & event.getActionMasked()) == MotionEvent.ACTION_DOWN) - { + if ((event.getAction() & event.getActionMasked()) == MotionEvent.ACTION_DOWN) { tapDisabled = false; } - + return super.onTouchEvent(event); } - + private float mX, mY; private static final float TOUCH_TOLERANCE = 2; private void touch_start(float x, float y) { - MuPDFView pageView = (MuPDFView)getDisplayedView(); - if (pageView != null) - { + MuPDFView pageView = (MuPDFView) getDisplayedView(); + if (pageView != null) { pageView.startDraw(x, y); } mX = x; @@ -209,22 +203,20 @@ private void touch_move(float x, float y) { float dx = Math.abs(x - mX); float dy = Math.abs(y - mY); - if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) - { - MuPDFView pageView = (MuPDFView)getDisplayedView(); - if (pageView != null) - { + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + MuPDFView pageView = (MuPDFView) getDisplayedView(); + if (pageView != null) { pageView.continueDraw(x, y); } mX = x; mY = y; } } - + public void releaseViews() { this.applyToChildren(new ReaderView.ViewMapper() { void applyToView(View view) { - ((MuPDFView)view).releaseBitmaps(); + ((MuPDFView) view).releaseBitmaps(); } }); } @@ -233,19 +225,15 @@ private void touch_up() { // NOOP } - + protected void onChildSetup(int i, View v) { - //TODO: page number in landscape - if (SearchTaskResult.get() != null && SearchTaskResult.get().pageNumber == i) { + if (SearchTaskResult.get() != null + && SearchTaskResult.get().pageNumber == i) ((MuPDFView) v).setSearchBoxes(SearchTaskResult.get().searchBoxes); - ((MuPDFView) v).setSearchBoxesPrim(SearchTaskResult.get().searchBoxesPrim); - } - else { + else ((MuPDFView) v).setSearchBoxes(null); - ((MuPDFView) v).setSearchBoxesPrim(null); - } - ((MuPDFView) v).setLinkHighlighting(mLinksHighlighted); + ((MuPDFView) v).setLinkHighlighting(mLinksEnabled); ((MuPDFView) v).setChangeReporter(new Runnable() { public void run() { @@ -260,7 +248,8 @@ void applyToView(View view) { } protected void onMoveToChild(int i) { - if (SearchTaskResult.get() != null && SearchTaskResult.get().pageNumber != i) { + if (SearchTaskResult.get() != null + && SearchTaskResult.get().pageNumber != i) { SearchTaskResult.set(null); resetupChildren(); } @@ -294,4 +283,4 @@ protected void onNotInUse(View v) { protected void onScaleChild(View v, Float scale) { ((MuPDFView) v).setScale(scale); } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/MuPDFReflowAdapter.java b/android/src/com/artifex/mupdflib/MuPDFReflowAdapter.java new file mode 100644 index 0000000..131715e --- /dev/null +++ b/android/src/com/artifex/mupdflib/MuPDFReflowAdapter.java @@ -0,0 +1,43 @@ +package com.artifex.mupdflib; + +import android.content.Context; +import android.graphics.Point; +import android.graphics.PointF; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +public class MuPDFReflowAdapter extends BaseAdapter { + private final Context mContext; + private final MuPDFCore mCore; + + public MuPDFReflowAdapter(Context c, MuPDFCore core) { + mContext = c; + mCore = core; + } + + public int getCount() { + return mCore.countPages(); + } + + public Object getItem(int arg0) { + return null; + } + + public long getItemId(int arg0) { + return 0; + } + + public View getView(int position, View convertView, ViewGroup parent) { + final MuPDFReflowView reflowView; + if (convertView == null) { + reflowView = new MuPDFReflowView(mContext, mCore, new Point(parent.getWidth(), parent.getHeight())); + } else { + reflowView = (MuPDFReflowView) convertView; + } + + reflowView.setPage(position, new PointF()); + + return reflowView; + } +} diff --git a/android/src/com/artifex/mupdflib/MuPDFReflowView.java b/android/src/com/artifex/mupdflib/MuPDFReflowView.java new file mode 100644 index 0000000..7fbcada --- /dev/null +++ b/android/src/com/artifex/mupdflib/MuPDFReflowView.java @@ -0,0 +1,182 @@ +package com.artifex.mupdflib; + +import android.content.Context; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.RectF; +import android.os.Handler; +import android.util.Base64; +import android.view.MotionEvent; +import android.view.View; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class MuPDFReflowView extends WebView implements MuPDFView { + private final MuPDFCore mCore; + private final Handler mHandler; + private final Point mParentSize; + private int mPage; + private float mScale; + private int mContentHeight; + AsyncTask mLoadHTML; + + public MuPDFReflowView(Context c, MuPDFCore core, Point parentSize) { + super(c); + mHandler = new Handler(); + mCore = core; + mParentSize = parentSize; + mScale = 1.0f; + mContentHeight = parentSize.y; + getSettings().setJavaScriptEnabled(true); + addJavascriptInterface(new Object(){ + public void reportContentHeight(String value) { + mContentHeight = (int)Float.parseFloat(value); + mHandler.post(new Runnable() { + public void run() { + requestLayout(); + } + }); + } + }, "HTMLOUT"); + setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + setScale(mScale); + } + }); + } + + private void requestHeight() { + // Get the webview to report the content height via the interface setup + // above. Workaround for getContentHeight not working + loadUrl("javascript:elem=document.getElementById('content');window.HTMLOUT.reportContentHeight("+mParentSize.x+"*elem.offsetHeight/elem.offsetWidth)"); + } + + public void setPage(int page, PointF size) { + mPage = page; + if (mLoadHTML != null) { + mLoadHTML.cancel(true); + } + mLoadHTML = new AsyncTask() { + @Override + protected byte[] doInBackground(Void... params) { + return mCore.html(mPage); + } + @Override + protected void onPostExecute(byte[] result) { + String b64 = Base64.encodeToString(result, Base64.DEFAULT); + loadData(b64, "text/html; charset=utf-8", "base64"); + } + }; + mLoadHTML.execute(); + } + + public int getPage() { + return mPage; + } + + public void setScale(float scale) { + mScale = scale; + loadUrl("javascript:document.getElementById('content').style.zoom=\""+(int)(mScale*100)+"%\""); + requestHeight(); + } + + public void blank(int page) { + } + + public Hit passClickEvent(float x, float y) { + return Hit.Nothing; + } + + public LinkInfo hitLink(float x, float y) { + return null; + } + + public void selectText(float x0, float y0, float x1, float y1) { + } + + public void deselectText() { + } + + public boolean copySelection() { + return false; + } + + public boolean markupSelection(Annotation.Type type) { + return false; + } + + public void startDraw(float x, float y) { + } + + public void continueDraw(float x, float y) { + } + + public void cancelDraw() { + } + + public boolean saveDraw() { + return false; + } + + public void setSearchBoxes(RectF[] searchBoxes) { + } + + public void setLinkHighlighting(boolean f) { + } + + public void deleteSelectedAnnotation() { + } + + public void deselectAnnotation() { + } + + public void setChangeReporter(Runnable reporter) { + } + + public void update() { + } + + public void updateHq(boolean update) { + } + + public void removeHq() { + } + + public void releaseResources() { + if (mLoadHTML != null) { + mLoadHTML.cancel(true); + mLoadHTML = null; + } + } + + public void releaseBitmaps() { + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int x, y; + switch(View.MeasureSpec.getMode(widthMeasureSpec)) { + case View.MeasureSpec.UNSPECIFIED: + x = mParentSize.x; + break; + default: + x = View.MeasureSpec.getSize(widthMeasureSpec); + } + switch(View.MeasureSpec.getMode(heightMeasureSpec)) { + case View.MeasureSpec.UNSPECIFIED: + y = mContentHeight; + break; + default: + y = View.MeasureSpec.getSize(heightMeasureSpec); + } + + setMeasuredDimension(x, y); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/android/src/com/artifex/mupdflib/MuPDFView.java b/android/src/com/artifex/mupdflib/MuPDFView.java index ce9d492..c2cd029 100644 --- a/android/src/com/artifex/mupdflib/MuPDFView.java +++ b/android/src/com/artifex/mupdflib/MuPDFView.java @@ -3,58 +3,31 @@ import android.graphics.PointF; import android.graphics.RectF; -enum Hit { - Nothing, Widget, Annotation -}; +enum Hit {Nothing, Widget, Annotation}; public interface MuPDFView { public void setPage(int page, PointF size); - public void setScale(float scale); - public int getPage(); - public void blank(int page); - public Hit passClickEvent(float x, float y); - public LinkInfo hitLink(float x, float y); - public void selectText(float x0, float y0, float x1, float y1); - public void deselectText(); - public boolean copySelection(); - public boolean markupSelection(Annotation.Type type); - public void deleteSelectedAnnotation(); - public void setSearchBoxes(RectF searchBoxes[]); - - public void setSearchBoxesPrim(RectF searchBoxes[]); - public void setLinkHighlighting(boolean f); - public void deselectAnnotation(); - public void startDraw(float x, float y); - public void continueDraw(float x, float y); - public void cancelDraw(); - public boolean saveDraw(); - public void setChangeReporter(Runnable reporter); - public void update(); - public void updateHq(boolean update); - public void removeHq(); - public void releaseResources(); - public void releaseBitmaps(); } diff --git a/android/src/com/artifex/mupdflib/OutlineItem.java b/android/src/com/artifex/mupdflib/OutlineItem.java index 974da7c..9e43372 100644 --- a/android/src/com/artifex/mupdflib/OutlineItem.java +++ b/android/src/com/artifex/mupdflib/OutlineItem.java @@ -1,14 +1,14 @@ package com.artifex.mupdflib; public class OutlineItem { - public final int level; + public final int level; public final String title; - public final int page; + public final int page; OutlineItem(int _level, String _title, int _page) { level = _level; title = _title; - page = _page; + page = _page; } } diff --git a/android/src/com/artifex/mupdflib/PDFPreviewGridActivity.java b/android/src/com/artifex/mupdflib/PDFPreviewGridActivity.java deleted file mode 100644 index d076844..0000000 --- a/android/src/com/artifex/mupdflib/PDFPreviewGridActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.artifex.mupdflib; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - -import android.app.Activity; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.GridView; - -public class PDFPreviewGridActivity extends Activity { - private MuPDFCore mCore; - private int mPosition; - private GridView mGrid; - private PDFPreviewGridAdapter mAdapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - LibraryUtils.reloadLocale(getApplicationContext()); - - mCore = PDFPreviewGridActivityData.get().core; - mPosition = PDFPreviewGridActivityData.get().position; - - try { - setContentView(TiRHelper - .getResource("layout.preview_grid_fragment")); - - mGrid = (GridView) findViewById(TiRHelper - .getResource("id.preview_grid")); - } catch (ResourceNotFoundException exp) { - Log.e("MuPDFPreviewGridActvity", "XML resouce not found!"); - } - - mAdapter = new PDFPreviewGridAdapter(this, mCore, mPosition); - mGrid.setAdapter(mAdapter); - mGrid.smoothScrollToPosition(mPosition); - } - - public void OnCancelPreviewButtonClick(View v) { - setResult(mPosition); - finish(); - } - -} diff --git a/android/src/com/artifex/mupdflib/PDFPreviewGridActivityData.java b/android/src/com/artifex/mupdflib/PDFPreviewGridActivityData.java deleted file mode 100644 index 42b3e44..0000000 --- a/android/src/com/artifex/mupdflib/PDFPreviewGridActivityData.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.artifex.mupdflib; - -public class PDFPreviewGridActivityData { - public MuPDFCore core; - public int position; - static private PDFPreviewGridActivityData singleton; - - static public void set(PDFPreviewGridActivityData d) { - singleton = d; - } - - static public PDFPreviewGridActivityData get() { - if (singleton == null) - singleton = new PDFPreviewGridActivityData(); - return singleton; - } -} diff --git a/android/src/com/artifex/mupdflib/PDFPreviewGridAdapter.java b/android/src/com/artifex/mupdflib/PDFPreviewGridAdapter.java deleted file mode 100644 index ebd4048..0000000 --- a/android/src/com/artifex/mupdflib/PDFPreviewGridAdapter.java +++ /dev/null @@ -1,311 +0,0 @@ -package com.artifex.mupdflib; - -import android.app.Activity; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.*; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.Log; -import android.util.SparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.lang.ref.WeakReference; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - -public class PDFPreviewGridAdapter extends BaseAdapter { - - private static final String TAG = PDFPreviewGridAdapter.class - .getSimpleName(); - private Context mContext; - private MuPDFCore mCore; - private int mPosition; - - private Point mPreviewSize; - private final SparseArray mBitmapCache = new SparseArray(); - private String mPath; - - private int currentlyViewing; - private Bitmap mLoadingBitmap; - - public PDFPreviewGridAdapter(Context context, MuPDFCore core, int position) { - mContext = context; - mCore = core; - mPosition = position; - - File documentCache = new File(StorageUtils.getCacheSubDirectory( - mContext, "previews2"), MD5.MD5Hash((new File(core - .getFileName())).getName())); - if (!documentCache.exists()) - documentCache.mkdirs(); - - mPath = documentCache.toString() + File.separator; - - try { - mLoadingBitmap = BitmapFactory.decodeResource( - mContext.getResources(), - TiRHelper.getResource("drawable.darkdenim3")); - } catch (ResourceNotFoundException exp) { - Log.e("MuPDFPreviewGridAdapter", "XML resouce not found!"); - } - } - - @Override - public int getCount() { - int count = mCore.countSinglePages(); - return count; - } - - @Override - public Object getItem(int pPosition) { - return null; - } - - @Override - public long getItemId(int pPosition) { - if (mCore.getDisplayPages() == 1) - return pPosition; - else if (pPosition > 0) - return (pPosition + 1) / 2; - else - return 0; - } - - public View getView(final int position, View convertView, ViewGroup parent) { - ViewHolder holder; - if (convertView == null) { - LayoutInflater inflater = (LayoutInflater) mContext - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - try { - convertView = inflater.inflate( - TiRHelper.getResource("layout.preview_grid_item"), - parent, false); - } catch (ResourceNotFoundException exp) { - Log.e("PDFPreviewGridAdapter", "XML resouce not found!"); - } - holder = new ViewHolder(convertView); - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); - } - - holder.previewPageProgress.setVisibility(ProgressBar.VISIBLE); - holder.previewPageImageView.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - ((Activity) mContext).setResult(position); - ((Activity) mContext).finish(); - } - }); - - holder.previewGridItemRelativeLayout - .setBackgroundColor(Color.TRANSPARENT); - drawPageImageView(holder, position); - return convertView; - } - - static class ViewHolder { - ImageView previewPageImageView = null; - ProgressBar previewPageProgress = null; - // TextView previewPageNumber = null; - RelativeLayout previewGridItemRelativeLayout = null; - - ViewHolder(View view) { - try { - this.previewPageImageView = (ImageView) view - .findViewById(TiRHelper - .getResource("id.preview_grid_image")); - this.previewPageProgress = (ProgressBar) view - .findViewById(TiRHelper - .getResource("id.preview_grid_image_progressbar")); - // this.previewPageNumber = (TextView) - // view.findViewById(R.id.PreviewPageNumber); - this.previewGridItemRelativeLayout = (RelativeLayout) view - .findViewById(TiRHelper - .getResource("id.PreviewGridItemRelativeLayout")); - } catch (ResourceNotFoundException exp) { - Log.e("PDFPreviewGridAdapter", "XML resouce not found!"); - } - } - } - - private void drawPageImageView(ViewHolder holder, int position) { - if (cancelPotentialWork(holder, position)) { - final BitmapWorkerTask task = new BitmapWorkerTask(holder, position); - final AsyncDrawable asyncDrawable = new AsyncDrawable( - mContext.getResources(), mLoadingBitmap, task); - holder.previewPageImageView.setImageDrawable(asyncDrawable); - task.execute(); - } - } - - public static boolean cancelPotentialWork(ViewHolder holder, int position) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(holder.previewPageImageView); - - if (bitmapWorkerTask != null) { - final int bitmapPosition = bitmapWorkerTask.position; - if (bitmapPosition != position) { - // Cancel previous task - bitmapWorkerTask.cancel(true); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was - // cancelled - return true; - } - - class BitmapWorkerTask extends AsyncTask { - - private final WeakReference viewHolderReference; - private int position; - - public BitmapWorkerTask(ViewHolder holder, int position) { - viewHolderReference = new WeakReference(holder); - this.position = position; - } - - @Override - protected Bitmap doInBackground(Integer... params) { - if (mPreviewSize == null) { - mPreviewSize = new Point(); - int padding = 0; - try { - padding = mContext.getResources().getDimensionPixelSize( - TiRHelper.getResource("dimen.preview_height")); - } catch (ResourceNotFoundException exp) { - Log.e("PDFPreviewGridAdapter", "XML resouce not found!"); - } - PointF mPageSize = mCore.getSinglePageSize(0); - float scale = mPageSize.y / mPageSize.x; - mPreviewSize.x = (int) ((float) padding / scale); - mPreviewSize.y = padding; - } - Bitmap lq = null; - lq = getCachedBitmap(position); - mBitmapCache.put(position, lq); - return lq; - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (isCancelled()) { - bitmap = null; - } - - if (viewHolderReference != null && bitmap != null) { - final ViewHolder holder = viewHolderReference.get(); - if (holder != null) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(holder.previewPageImageView); - if (this == bitmapWorkerTask && holder != null) { - holder.previewPageImageView.setImageBitmap(bitmap); - holder.previewPageProgress - .setVisibility(ProgressBar.GONE); - // holder.previewPageNumber.setText(String.valueOf(position - // + 1)); - - if (mPosition == position) { - try { - holder.previewGridItemRelativeLayout - .setBackgroundColor(mContext - .getResources() - .getColor( - TiRHelper - .getResource("color.thumbnail_selected_background"))); - } catch (ResourceNotFoundException exp) { - Log.e("PDFPreviewGridAdapter", - "XML resouce not found!"); - } - } else { - holder.previewGridItemRelativeLayout - .setBackgroundColor(Color.TRANSPARENT); - } - } - } - } - } - } - - private Bitmap getCachedBitmap(int position) { - String mCachedBitmapFilePath = mPath + position; - File mCachedBitmapFile = new File(mCachedBitmapFilePath); - Bitmap lq = null; - try { - if (mCachedBitmapFile.exists() && mCachedBitmapFile.canRead()) { - Log.d(TAG, "page " + position + " found in cache"); - lq = BitmapFactory.decodeFile(mCachedBitmapFilePath); - return lq; - } - } catch (Exception e) { - e.printStackTrace(); - // some error with cached file, - // delete the file and get rid of bitmap - mCachedBitmapFile.delete(); - lq = null; - } - if (lq == null) { - lq = Bitmap.createBitmap(mPreviewSize.x, mPreviewSize.y, - Bitmap.Config.ARGB_8888); - mCore.drawSinglePage(position, lq, mPreviewSize.x, mPreviewSize.y); - try { - lq.compress(CompressFormat.JPEG, 70, new FileOutputStream( - mCachedBitmapFile)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - mCachedBitmapFile.delete(); - } - } - return lq; - } - - public int getCurrentlyViewing() { - return currentlyViewing; - } - - public void setCurrentlyViewing(int currentlyViewing) { - this.currentlyViewing = currentlyViewing; - notifyDataSetChanged(); - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, - BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference( - bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } -} diff --git a/android/src/com/artifex/mupdflib/PageView.java b/android/src/com/artifex/mupdflib/PageView.java index 22dbb04..f18013e 100644 --- a/android/src/com/artifex/mupdflib/PageView.java +++ b/android/src/com/artifex/mupdflib/PageView.java @@ -1,38 +1,29 @@ package com.artifex.mupdflib; -import android.annotation.SuppressLint; +import java.util.ArrayList; +import java.util.Iterator; + +import org.appcelerator.titanium.util.TiRHelper; +import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; + import android.content.Context; -import android.graphics.*; +import android.graphics.Bitmap; import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; -import java.util.ArrayList; -import java.util.Iterator; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; -/* -class PatchInfo { - //public BitmapHolder bmh; - //public Bitmap bm; - public Point patchViewSize; - public Rect patchArea; - public boolean completeRedraw; - - public PatchInfo(Point aPatchViewSize, Rect aPatchArea, boolean aCompleteRedraw) { - //bmh = aBmh; - //bm = null; - patchViewSize = aPatchViewSize; - patchArea = aPatchArea; - completeRedraw = aCompleteRedraw; - } -} -*/ // Make our ImageViews opaque to optimize redraw class OpaqueImageView extends ImageView { @@ -69,7 +60,8 @@ public void select(TextProcessor tp) { ArrayList lines = new ArrayList(); for (TextWord[] line : mText) - if (line[0].bottom > mSelectBox.top && line[0].top < mSelectBox.bottom) + if (line[0].bottom > mSelectBox.top + && line[0].top < mSelectBox.bottom) lines.add(line); Iterator it = lines.iterator(); @@ -102,7 +94,7 @@ public void select(TextProcessor tp) { public abstract class PageView extends ViewGroup { public static int HIGHLIGHT_COLOR = 0x802572AC; - private static final int LINK_COLOR = 0x20AC7225; + private static final int LINK_COLOR = 0x80AC7225; private static final int BOX_COLOR = 0xFF4444FF; private static final int INK_COLOR = 0xFFFF0000; private static final float INK_THICKNESS = 10.0f; @@ -115,28 +107,19 @@ public abstract class PageView extends ViewGroup { protected float mSourceScale; private ImageView mEntire; // Image rendered at minimum zoom - //private BitmapHolder mEntireBmh; - private Bitmap mEntireBm; - private Matrix mEntireMat; + private Bitmap mEntireBm; + private Matrix mEntireMat; private AsyncTask mGetText; private AsyncTask mGetLinkInfo; - //private AsyncTask mDrawEntire; - //private AsyncTask mDrawEntire; - private CancellableAsyncTask mDrawEntire; - - + private CancellableAsyncTask mDrawEntire; private Point mPatchViewSize; // View size on the basis of which the patch // was created private Rect mPatchArea; private ImageView mPatch; - //private BitmapHolder mPatchBmh; - private Bitmap mPatchBm; - //private AsyncTask mDrawPatch; - private CancellableAsyncTask mDrawPatch; - + private Bitmap mPatchBm; + private CancellableAsyncTask mDrawPatch; private RectF mSearchBoxes[]; - private RectF mSearchBoxesPrim[]; protected LinkInfo mLinks[]; private RectF mSelectBox; private TextWord mText[][]; @@ -149,36 +132,24 @@ public abstract class PageView extends ViewGroup { private ProgressBar mBusyIndicator; private final Handler mHandler = new Handler(); - //public PageView(Context c, Point parentSize) { public PageView(Context c, Point parentSize, Bitmap sharedHqBm) { - super(c); mContext = c; mParentSize = parentSize; setBackgroundColor(BACKGROUND_COLOR); - //mEntireBmh = new BitmapHolder(); - //mPatchBmh = new BitmapHolder(); - try { - mEntireBm = Bitmap.createBitmap(parentSize.x, parentSize.y, Config.ARGB_8888); - } - catch ( OutOfMemoryError e) { - Log.d("MY_OOM_ERROR", "error"); - } - + mEntireBm = Bitmap.createBitmap(parentSize.x, parentSize.y, + Config.ARGB_8888); mPatchBm = sharedHqBm; mEntireMat = new Matrix(); - } - //protected abstract Bitmap drawPage(int sizeX, int sizeY, int patchX, - // int patchY, int patchWidth, int patchHeight); + protected abstract CancellableTaskDefinition getDrawPageTask( + Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, + int patchWidth, int patchHeight); - //protected abstract Bitmap updatePage(BitmapHolder h, int sizeX, int sizeY, - // int patchX, int patchY, int patchWidth, int patchHeight); - //protected abstract void drawPage(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); - //protected abstract void updatePage(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); - protected abstract CancellableTaskDefinition getDrawPageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); - protected abstract CancellableTaskDefinition getUpdatePageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight); + protected abstract CancellableTaskDefinition getUpdatePageTask( + Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, + int patchWidth, int patchHeight); protected abstract LinkInfo[] getLinkInfo(); @@ -189,13 +160,11 @@ public PageView(Context c, Point parentSize, Bitmap sharedHqBm) { private void reinit() { // Cancel pending render task if (mDrawEntire != null) { - //mDrawEntire.cancel(true); mDrawEntire.cancelAndWait(); mDrawEntire = null; } if (mDrawPatch != null) { - //mDrawPatch.cancel(true); mDrawPatch.cancelAndWait(); mDrawPatch = null; } @@ -218,13 +187,11 @@ private void reinit() { if (mEntire != null) { mEntire.setImageBitmap(null); - //mEntireBmh.setBm(null); mEntire.invalidate(); } if (mPatch != null) { mPatch.setImageBitmap(null); - //mPatchBmh.setBm(null); mPatch.invalidate(); } @@ -232,7 +199,6 @@ private void reinit() { mPatchArea = null; mSearchBoxes = null; - mSearchBoxesPrim = null; mLinks = null; mSelectBox = null; mText = null; @@ -247,11 +213,9 @@ public void releaseResources() { mBusyIndicator = null; } } - + public void releaseBitmaps() { reinit(); - mEntireBm.recycle(); - mPatchBm.recycle(); mEntireBm = null; mPatchBm = null; } @@ -264,20 +228,19 @@ public void blank(int page) { mBusyIndicator = new ProgressBar(mContext); mBusyIndicator.setIndeterminate(true); try { - mBusyIndicator.setBackgroundResource(TiRHelper.getResource("drawable.busy")); - } catch( ResourceNotFoundException e ) - { + mBusyIndicator.setBackgroundResource(TiRHelper + .getResource("drawable.busy")); + } catch (ResourceNotFoundException e) { } - addView(mBusyIndicator); } + setBackgroundColor(BACKGROUND_COLOR); } public void setPage(int page, PointF size) { // Cancel pending render task if (mDrawEntire != null) { - //mDrawEntire.cancel(true); mDrawEntire.cancelAndWait(); mDrawEntire = null; } @@ -290,11 +253,10 @@ public void setPage(int page, PointF size) { mPageNumber = page; if (mEntire == null) { mEntire = new OpaqueImageView(mContext); - //mEntire.setScaleType(ImageView.ScaleType.FIT_CENTER); mEntire.setScaleType(ImageView.ScaleType.MATRIX); addView(mEntire); } - + // Calculate scaled size that fits within the screen limits // This is the size at minimum zoom mSourceScale = Math.min(mParentSize.x / size.x, mParentSize.y / size.y); @@ -303,7 +265,6 @@ public void setPage(int page, PointF size) { mSize = newSize; mEntire.setImageBitmap(null); - //mEntireBmh.setBm(null); mEntire.invalidate(); // Get the link info in the background @@ -314,41 +275,31 @@ protected LinkInfo[] doInBackground(Void... v) { protected void onPostExecute(LinkInfo[] v) { mLinks = v; - //invalidate(); if (mSearchView != null) mSearchView.invalidate(); - } }; mGetLinkInfo.execute(); // Render the page in the background - //mDrawEntire = new AsyncTask() { - // protected Void doInBackground(Void... v) { - // drawPage(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y); - // return null; - // } - mDrawEntire = new CancellableAsyncTask(getDrawPageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { - - //protected void onPreExecute() { + mDrawEntire = new CancellableAsyncTask(getDrawPageTask( + mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { + @Override public void onPreExecute() { setBackgroundColor(BACKGROUND_COLOR); mEntire.setImageBitmap(null); - //mEntireBmh.setBm(null); mEntire.invalidate(); if (mBusyIndicator == null) { mBusyIndicator = new ProgressBar(mContext); mBusyIndicator.setIndeterminate(true); - try{ - mBusyIndicator.setBackgroundResource(TiRHelper.getResource("drawable.busy")); - } catch ( ResourceNotFoundException e ) - { - + try { + mBusyIndicator.setBackgroundResource(TiRHelper + .getResource("drawable.busy")); + } catch (ResourceNotFoundException e) { } - addView(mBusyIndicator); mBusyIndicator.setVisibility(INVISIBLE); mHandler.postDelayed(new Runnable() { @@ -360,17 +311,14 @@ public void run() { } } - //protected void onPostExecute(Void v) { @Override public void onPostExecute(Void result) { removeView(mBusyIndicator); mBusyIndicator = null; - //mEntire.setImageBitmap(bm); - //mEntireBmh.setBm(bm); mEntire.setImageBitmap(mEntireBm); mEntire.invalidate(); - //invalidate(); setBackgroundColor(Color.TRANSPARENT); + } }; @@ -378,13 +326,13 @@ public void onPostExecute(Void result) { if (mSearchView == null) { mSearchView = new View(mContext) { - @SuppressLint("DrawAllocation") @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); // Work out current total scale factor // from source to view - final float scale = mSourceScale * (float) getWidth() / (float) mSize.x; + final float scale = mSourceScale * (float) getWidth() + / (float) mSize.x; final Paint paint = new Paint(); if (!mIsBlank && mSearchBoxes != null) { @@ -395,29 +343,13 @@ protected void onDraw(final Canvas canvas) { rect.bottom * scale, paint); } - if (!mIsBlank && mSearchBoxesPrim != null) { - paint.setColor(HIGHLIGHT_COLOR); - for (RectF rect : mSearchBoxesPrim) - canvas.drawRect(rect.left * scale + getWidth() / 2, rect.top * scale, - rect.right * scale + getWidth() / 2, rect.bottom * scale, paint); - } - if (!mIsBlank && mLinks != null && mHighlightLinks) { - paint.setStrokeWidth(2); + paint.setColor(LINK_COLOR); for (LinkInfo link : mLinks) - { - //canvas.drawRect(link.rect.left * scale, link.rect.top * scale, - // link.rect.right * scale, link.rect.bottom * scale, paint); - RectF rectfa = new RectF((link.rect.left - 2) * scale, (link.rect.top - 2) * scale, - (link.rect.right + 2) * scale, (link.rect.bottom + 2) * scale); - paint.setStyle(Paint.Style.FILL); - paint.setColor(LINK_COLOR); - canvas.drawRoundRect(rectfa, 3 * scale, 3 * scale, paint); - - paint.setStyle(Paint.Style.STROKE); - paint.setColor(HIGHLIGHT_COLOR); - canvas.drawRoundRect(rectfa, 3 * scale, 3 * scale, paint); - } + canvas.drawRect(link.rect.left * scale, + link.rect.top * scale, link.rect.right + * scale, link.rect.bottom * scale, + paint); } if (mSelectBox != null && mText != null) { @@ -477,14 +409,16 @@ public void onEndLine() { p = iit.next(); float x = p.x * scale; float y = p.y * scale; - path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2); + path.quadTo(mX, mY, (x + mX) / 2, + (y + mY) / 2); mX = x; mY = y; } path.lineTo(mX, mY); } else { p = arc.get(0); - canvas.drawCircle(p.x * scale, p.y * scale, INK_THICKNESS * scale / 2, paint); + canvas.drawCircle(p.x * scale, p.y * scale, + INK_THICKNESS * scale / 2, paint); } } @@ -505,12 +439,6 @@ public void setSearchBoxes(RectF searchBoxes[]) { mSearchView.invalidate(); } - public void setSearchBoxesPrim(RectF searchBoxes[]) { - mSearchBoxesPrim = searchBoxes; - if (mSearchView != null) - mSearchView.invalidate(); - } - public void setLinkHighlighting(boolean f) { mHighlightLinks = f; if (mSearchView != null) @@ -642,11 +570,8 @@ protected void onLayout(boolean changed, int left, int top, int right, int h = bottom - top; if (mEntire != null) { - //mEntireMat.setScale(w/(float)mSize.x, h/(float)mSize.y); - //mEntire.setImageMatrix(mEntireMat); - //mEntire.invalidate(); if (mEntire.getWidth() != w || mEntire.getHeight() != h) { - mEntireMat.setScale(w/(float)mSize.x, h/(float)mSize.y); + mEntireMat.setScale(w / (float) mSize.x, h / (float) mSize.y); mEntire.setImageMatrix(mEntireMat); mEntire.invalidate(); } @@ -664,7 +589,6 @@ protected void onLayout(boolean changed, int left, int top, int right, mPatchArea = null; if (mPatch != null) { mPatch.setImageBitmap(null); - //mPatchBmh.setBm(null); mPatch.invalidate(); } } else { @@ -684,18 +608,16 @@ protected void onLayout(boolean changed, int left, int top, int right, public void updateHq(boolean update) { Rect viewArea = new Rect(getLeft(), getTop(), getRight(), getBottom()); - // If the viewArea's size matches the unzoomed size, there is no need - // for an hq patch if (viewArea.width() == mSize.x || viewArea.height() == mSize.y) { - // If the viewArea's size matches the unzoomed size, there is no need for an hq patch + // If the viewArea's size matches the unzoomed size, there is no + // need for an hq patch if (mPatch != null) { mPatch.setImageBitmap(null); mPatch.invalidate(); } } else { - //Point patchViewSize = new Point(viewArea.width(), viewArea.height()); - //Rect patchArea = new Rect(0, 0, mParentSize.x, mParentSize.y); - final Point patchViewSize = new Point(viewArea.width(), viewArea.height()); + final Point patchViewSize = new Point(viewArea.width(), + viewArea.height()); final Rect patchArea = new Rect(0, 0, mParentSize.x, mParentSize.y); // Intersect and test that there is an intersection @@ -717,138 +639,68 @@ public void updateHq(boolean update) { // Stop the drawing of previous patch if still going if (mDrawPatch != null) { - //mDrawPatch.cancel(true); mDrawPatch.cancelAndWait(); mDrawPatch = null; } - //if (completeRedraw) { - // The bitmap holder mPatchBm may still be rendered to by a - // previously invoked task, and possibly for a different - // area, so we cannot risk the bitmap generated by this task - // being passed to it - // mPatchBmh.drop(); - // mPatchBmh = new BitmapHolder(); - //} - // Create and add the image view if not already done if (mPatch == null) { mPatch = new OpaqueImageView(mContext); - //mPatch.setScaleType(ImageView.ScaleType.FIT_CENTER); mPatch.setScaleType(ImageView.ScaleType.MATRIX); addView(mPatch); - if (mSearchView != null) - mSearchView.bringToFront(); + mSearchView.bringToFront(); } + CancellableTaskDefinition task; + if (completeRedraw) - task = getDrawPageTask(mPatchBm, patchViewSize.x, patchViewSize.y, - patchArea.left, patchArea.top, - patchArea.width(), patchArea.height()); + task = getDrawPageTask(mPatchBm, patchViewSize.x, + patchViewSize.y, patchArea.left, patchArea.top, + patchArea.width(), patchArea.height()); else - task = getUpdatePageTask(mPatchBm, patchViewSize.x, patchViewSize.y, - patchArea.left, patchArea.top, + task = getUpdatePageTask(mPatchBm, patchViewSize.x, + patchViewSize.y, patchArea.left, patchArea.top, patchArea.width(), patchArea.height()); - mDrawPatch = new CancellableAsyncTask(task) { - /* - mDrawPatch = new AsyncTask() { - protected PatchInfo doInBackground(PatchInfo... v) { - if (v[0].completeRedraw) { - //v[0].bm = drawPage(v[0].patchViewSize.x, - drawPage(mPatchBm, v[0].patchViewSize.x, - v[0].patchViewSize.y, v[0].patchArea.left, - v[0].patchArea.top, v[0].patchArea.width(), - v[0].patchArea.height()); - } else { - //v[0].bm = updatePage(v[0].bmh, v[0].patchViewSize.x, - updatePage(mPatchBm, v[0].patchViewSize.x, - v[0].patchViewSize.y, v[0].patchArea.left, - v[0].patchArea.top, v[0].patchArea.width(), - v[0].patchArea.height()); - } + mDrawPatch = new CancellableAsyncTask(task) { - return v[0]; - } - */ public void onPostExecute(Void result) { mPatchViewSize = patchViewSize; - mPatchArea = patchArea; - - //protected void onPostExecute(PatchInfo v) { - //if (mPatchBmh == v.bmh) { - // mPatchViewSize = v.patchViewSize; - // mPatchArea = v.patchArea; - // if (v.bm != null) { - // mPatch.setImageBitmap(v.bm); - // v.bmh.setBm(v.bm); - // v.bm = null; - // } - // requestLayout(); - // Calling requestLayout here doesn't lead to a later - // call to layout. No idea - // why, but apparently others have run into the problem. - // mPatch.layout(mPatchArea.left, mPatchArea.top, - // mPatchArea.right, mPatchArea.bottom); - // invalidate(); - //mPatchViewSize = v.patchViewSize; - //mPatchArea = v.patchArea; + mPatchArea = patchArea; mPatch.setImageBitmap(mPatchBm); mPatch.invalidate(); - //requestLayout(); - // Calling requestLayout here doesn't lead to a later call to layout. No idea + // requestLayout(); + // Calling requestLayout here doesn't lead to a later call + // to layout. No idea // why, but apparently others have run into the problem. - mPatch.layout(mPatchArea.left, mPatchArea.top, mPatchArea.right, mPatchArea.bottom); - //invalidate(); + mPatch.layout(mPatchArea.left, mPatchArea.top, + mPatchArea.right, mPatchArea.bottom); } }; - //mDrawPatch.execute(new PatchInfo(patchViewSize, patchArea, mPatchBmh, completeRedraw)); - //mDrawPatch.execute(new PatchInfo(patchViewSize, patchArea, completeRedraw)); mDrawPatch.execute(); - - } } public void update() { // Cancel pending render task if (mDrawEntire != null) { - //mDrawEntire.cancel(true); mDrawEntire.cancelAndWait(); mDrawEntire = null; } if (mDrawPatch != null) { - //mDrawPatch.cancel(true); mDrawPatch.cancelAndWait(); mDrawPatch = null; } // Render the page in the background - mDrawEntire = new CancellableAsyncTask(getUpdatePageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { - //mDrawEntire = new AsyncTask() { - // protected Void doInBackground(Void... v) { - // Pass the current bitmap as a basis for the update, but use a - // bitmap - // holder so that the held bitmap will be nulled and not hold on - // to - // memory, should this view become redundant. - // updatePage(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y); - // return null; - // } - - //protected void onPostExecute(Bitmap bm) { - // if (bm != null) { - // mEntire.setImageBitmap(bm); - // mEntireBmh.setBm(bm); - // } - // invalidate(); - //protected void onPostExecute(Void v) { + mDrawEntire = new CancellableAsyncTask(getUpdatePageTask( + mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) { + public void onPostExecute(Void result) { mEntire.setImageBitmap(mEntireBm); mEntire.invalidate(); - } }; @@ -860,7 +712,6 @@ public void onPostExecute(Void result) { public void removeHq() { // Stop the drawing of the patch if still going if (mDrawPatch != null) { - //mDrawPatch.cancel(true); mDrawPatch.cancelAndWait(); mDrawPatch = null; } @@ -870,7 +721,6 @@ public void removeHq() { mPatchArea = null; if (mPatch != null) { mPatch.setImageBitmap(null); - //mPatchBmh.setBm(null); mPatch.invalidate(); } } @@ -883,4 +733,4 @@ public int getPage() { public boolean isOpaque() { return true; } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/PrintDialogActivity.java b/android/src/com/artifex/mupdflib/PrintDialogActivity.java index 4c9480f..4fcc27d 100644 --- a/android/src/com/artifex/mupdflib/PrintDialogActivity.java +++ b/android/src/com/artifex/mupdflib/PrintDialogActivity.java @@ -1,5 +1,11 @@ package com.artifex.mupdflib; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import org.appcelerator.titanium.util.TiRHelper; +import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; + import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; @@ -11,12 +17,6 @@ import android.webkit.WebView; import android.webkit.WebViewClient; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - public class PrintDialogActivity extends Activity { private static final String PRINT_DIALOG_URL = "https://www.google.com/cloudprint/dialog.html"; private static final String JS_INTERFACE = "AndroidPrintDialog"; @@ -52,19 +52,22 @@ public void onCreate(Bundle icicle) { setContentView(TiRHelper.getResource("layout.print_dialog")); dialogWebView = (WebView) findViewById(TiRHelper .getResource("id.webview")); - } catch (ResourceNotFoundException exp) { - Log.e("PrintDialogAlert", "XML resouce not found!"); - } - cloudPrintIntent = this.getIntent(); - WebSettings settings = dialogWebView.getSettings(); - settings.setJavaScriptEnabled(true); + dialogWebView = (WebView) findViewById(TiRHelper + .getResource("id.webview")); + cloudPrintIntent = this.getIntent(); + + WebSettings settings = dialogWebView.getSettings(); + settings.setJavaScriptEnabled(true); - dialogWebView.setWebViewClient(new PrintDialogWebClient()); - dialogWebView.addJavascriptInterface( - new PrintDialogJavaScriptInterface(), JS_INTERFACE); + dialogWebView.setWebViewClient(new PrintDialogWebClient()); + dialogWebView.addJavascriptInterface( + new PrintDialogJavaScriptInterface(), JS_INTERFACE); - dialogWebView.loadUrl(PRINT_DIALOG_URL); + dialogWebView.loadUrl(PRINT_DIALOG_URL); + } catch (ResourceNotFoundException exp) { + Log.e("PrintDialogAlert", "XML resouce not found!"); + } } @Override diff --git a/android/src/com/artifex/mupdflib/ReaderView.java b/android/src/com/artifex/mupdflib/ReaderView.java index f566283..c4831c1 100644 --- a/android/src/com/artifex/mupdflib/ReaderView.java +++ b/android/src/com/artifex/mupdflib/ReaderView.java @@ -1,5 +1,11 @@ package com.artifex.mupdflib; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +import org.appcelerator.titanium.util.TiRHelper; +import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; + import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -13,15 +19,8 @@ import android.widget.AdapterView; import android.widget.Scroller; -import java.util.LinkedList; -import java.util.NoSuchElementException; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - public class ReaderView extends AdapterView implements GestureDetector.OnGestureListener, - GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener, Runnable { private static final int MOVING_DIAGONALLY = 0; private static final int MOVING_LEFT = 1; @@ -32,10 +31,12 @@ public class ReaderView extends AdapterView implements private static final int FLING_MARGIN = 100; private static final int GAP = 20; - private static final float MIN_SCALE = 0.97f; - private static final float MAX_SCALE = 4.0f; + private static final float MIN_SCALE = 1.0f; + private static final float MAX_SCALE = 5.0f; private static final float REFLOW_SCALE_FACTOR = 0.5f; + private static final boolean HORIZONTAL_SCROLLING = true; + private Adapter mAdapter; private int mCurrent; // Adapter's index for the current view private boolean mResetLayout; @@ -45,7 +46,7 @@ public class ReaderView extends AdapterView implements private final LinkedList mViewCache = new LinkedList(); private boolean mUserInteracting; // Whether the user is interacting private boolean mScaling; // Whether the user is currently pinch zooming - private float mScale = 0.97f; + private float mScale = 1.0f; private int mXScroll; // Scroll amounts recorded from events. private int mYScroll; // and then accounted for in onLayout private boolean mReflow = false; @@ -56,7 +57,8 @@ public class ReaderView extends AdapterView implements private final Stepper mStepper; private int mScrollerLastX; private int mScrollerLastY; - //private boolean mScrollDisabled; + private float mLastScaleFocusX; + private float mLastScaleFocusY; static abstract class ViewMapper { abstract void applyToView(View view); @@ -64,35 +66,30 @@ static abstract class ViewMapper { public ReaderView(Context context) { super(context); - //mGestureDetector = new GestureDetector(this); - //mScaleGestureDetector = new ScaleGestureDetector(context, this); - //mScroller = new Scroller(context); - //mStepper = new Stepper(this, this); - // "Edit mode" means when the View is being displayed in the Android GUI editor. (this class - // is instantiated in the IDE, so we need to be a bit careful what we do). - if (isInEditMode()) - { + mGestureDetector = new GestureDetector(this); + mScaleGestureDetector = new ScaleGestureDetector(context, this); + mScroller = new Scroller(context); + mStepper = new Stepper(this, this); + } + + public ReaderView(Context context, AttributeSet attrs) { + super(context, attrs); + + // "Edit mode" means when the View is being displayed in the Android GUI + // editor. (this class + // is instantiated in the IDE, so we need to be a bit careful what we + // do). + if (isInEditMode()) { mGestureDetector = null; mScaleGestureDetector = null; mScroller = null; mStepper = null; - } - else - { + } else { mGestureDetector = new GestureDetector(this); mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); + mScroller = new Scroller(context); mStepper = new Stepper(this, this); } - - } - - public ReaderView(Context context, AttributeSet attrs) { - super(context, attrs); - mGestureDetector = new GestureDetector(this); - mScaleGestureDetector = new ScaleGestureDetector(context, this); - mScroller = new Scroller(context); - mStepper = new Stepper(this, this); } public ReaderView(Context context, AttributeSet attrs, int defStyle) { @@ -323,7 +320,8 @@ public void smartMoveBackwards() { yOffset = -smartAdvanceAmount(screenHeight, top); } mScrollerLastX = mScrollerLastY = 0; - mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400); + mScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, + 400); mStepper.prod(); } @@ -342,18 +340,9 @@ public void refresh(boolean reflow) { mReflowChanged = true; mResetLayout = true; - mScale = 0.97f; + mScale = 1.0f; mXScroll = mYScroll = 0; - //int numChildren = mChildViews.size(); - //for (int i = 0; i < numChildren; i++) { - // View v = mChildViews.valueAt(i); - // onNotInUse(v); - // removeViewInLayout(v); - //} - //mChildViews.clear(); - //mViewCache.clear(); - requestLayout(); } @@ -413,7 +402,6 @@ public boolean onDown(MotionEvent arg0) { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - //if (mScrollDisabled) if (mScaling) return true; @@ -422,7 +410,7 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, Rect bounds = getScrollBounds(v); switch (directionOfTravel(velocityX, velocityY)) { case MOVING_LEFT: - if (bounds.left >= 0) { + if (HORIZONTAL_SCROLLING && bounds.left >= 0) { // Fling off to the left bring next view onto screen View vl = mChildViews.get(mCurrent + 1); @@ -432,8 +420,19 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, } } break; + case MOVING_UP: + if (!HORIZONTAL_SCROLLING && bounds.top >= 0) { + // Fling off to the top bring next view onto screen + View vl = mChildViews.get(mCurrent + 1); + + if (vl != null) { + slideViewOntoScreen(vl); + return true; + } + } + break; case MOVING_RIGHT: - if (bounds.right <= 0) { + if (HORIZONTAL_SCROLLING && bounds.right <= 0) { // Fling off to the right bring previous view onto screen View vr = mChildViews.get(mCurrent - 1); @@ -443,6 +442,17 @@ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, } } break; + case MOVING_DOWN: + if (!HORIZONTAL_SCROLLING && bounds.bottom <= 0) { + // Fling off to the bottom bring previous view onto screen + View vr = mChildViews.get(mCurrent - 1); + + if (vr != null) { + slideViewOntoScreen(vr); + return true; + } + } + break; } mScrollerLastX = mScrollerLastY = 0; // If the page has been dragged out of bounds then we want to spring @@ -479,7 +489,6 @@ public void onLongPress(MotionEvent e) { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - //if (!mScrollDisabled) { if (!mScaling) { mXScroll -= distanceX; mYScroll -= distanceY; @@ -505,12 +514,6 @@ public boolean onScale(ScaleGestureDetector detector) { max_scale); if (mReflow) { - //applyToChildren(new ViewMapper() { - // @Override - // void applyToView(View view) { - // onScaleChild(view, mScale); - // } - //}); View v = mChildViews.get(mCurrent); if (v != null) onScaleChild(v, mScale); @@ -519,14 +522,22 @@ public boolean onScale(ScaleGestureDetector detector) { View v = mChildViews.get(mCurrent); if (v != null) { + float currentFocusX = detector.getFocusX(); + float currentFocusY = detector.getFocusY(); // Work out the focus point relative to the view top left - int viewFocusX = (int) detector.getFocusX() - - (v.getLeft() + mXScroll); - int viewFocusY = (int) detector.getFocusY() - - (v.getTop() + mYScroll); + int viewFocusX = (int) currentFocusX - (v.getLeft() + mXScroll); + int viewFocusY = (int) currentFocusY - (v.getTop() + mYScroll); // Scroll to maintain the focus point mXScroll += viewFocusX - viewFocusX * factor; mYScroll += viewFocusY - viewFocusY * factor; + + if (mLastScaleFocusX >= 0) + mXScroll += currentFocusX - mLastScaleFocusX; + if (mLastScaleFocusY >= 0) + mYScroll += currentFocusY - mLastScaleFocusY; + + mLastScaleFocusX = currentFocusX; + mLastScaleFocusY = currentFocusY; requestLayout(); } } @@ -539,9 +550,7 @@ public boolean onScaleBegin(ScaleGestureDetector detector) { // screen is not showing the effect of them, so they can // only confuse the user mXScroll = mYScroll = 0; - // Avoid jump at end of scaling by disabling scrolling - // until the next start of gesture - //mScrollDisabled = true; + mLastScaleFocusX = mLastScaleFocusY = -1; return true; } @@ -560,17 +569,12 @@ void applyToView(View view) { @Override public boolean onTouchEvent(MotionEvent event) { mScaleGestureDetector.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); - //if (!mScaling) - mGestureDetector.onTouchEvent(event); - - //if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { mUserInteracting = true; } - //if (event.getActionMasked() == MotionEvent.ACTION_UP) { if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) { - //mScrollDisabled = false; mUserInteracting = false; View v = mChildViews.get(mCurrent); @@ -590,7 +594,6 @@ public boolean onTouchEvent(MotionEvent event) { } } - requestLayout(); return true; } @@ -608,8 +611,10 @@ protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - // "Edit mode" means when the View is being displayed in the Android GUI editor. (this class - // is instantiated in the IDE, so we need to be a bit careful what we do). + // "Edit mode" means when the View is being displayed in the Android GUI + // editor. (this class + // is instantiated in the IDE, so we need to be a bit careful what we + // do). if (isInEditMode()) return; @@ -619,12 +624,17 @@ protected void onLayout(boolean changed, int left, int top, int right, if (!mResetLayout) { // Move to next or previous if current is sufficiently off center if (cv != null) { + boolean move; cvOffset = subScreenSizeOffset(cv); // cv.getRight() may be out of date with the current scale // so add left to the measured width for the correct position - if (cv.getLeft() + cv.getMeasuredWidth() + cvOffset.x + GAP / 2 - + mXScroll < getWidth() / 2 - && mCurrent + 1 < mAdapter.getCount()) { + if (HORIZONTAL_SCROLLING) + move = cv.getLeft() + cv.getMeasuredWidth() + cvOffset.x + + GAP / 2 + mXScroll < getWidth() / 2; + else + move = cv.getTop() + cv.getMeasuredHeight() + cvOffset.y + + GAP / 2 + mYScroll < getHeight() / 2; + if (move && mCurrent + 1 < mAdapter.getCount()) { postUnsettle(cv); // post to invoke test for end of animation // where we must set hq area for the new current view @@ -635,8 +645,11 @@ protected void onLayout(boolean changed, int left, int top, int right, onMoveToChild(mCurrent); } - if (cv.getLeft() - cvOffset.x - GAP / 2 + mXScroll >= getWidth() / 2 - && mCurrent > 0) { + if (HORIZONTAL_SCROLLING) + move = cv.getLeft() - cvOffset.x - GAP / 2 + mXScroll >= getWidth() / 2; + else + move = cv.getTop() - cvOffset.y - GAP / 2 + mYScroll >= getHeight() / 2; + if (move && mCurrent > 0) { postUnsettle(cv); // post to invoke test for end of animation // where we must set hq area for the new current view @@ -717,13 +730,21 @@ protected void onLayout(boolean changed, int left, int top, int right, cvLeft += corr.x; cvTop += corr.y; cvBottom += corr.y; - } else if (cv.getMeasuredHeight() <= getHeight()) { + } else if (HORIZONTAL_SCROLLING + && cv.getMeasuredHeight() <= getHeight()) { // When the current view is as small as the screen in height, clamp // it vertically Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom)); cvTop += corr.y; cvBottom += corr.y; + } else if (!HORIZONTAL_SCROLLING && cv.getMeasuredWidth() <= getWidth()) { + // When the current view is as small as the screen in width, clamp + // it horizontally + Point corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, + cvBottom)); + cvRight += corr.x; + cvLeft += corr.x; } cv.layout(cvLeft, cvTop, cvRight, cvBottom); @@ -731,21 +752,36 @@ protected void onLayout(boolean changed, int left, int top, int right, if (mCurrent > 0) { View lv = getOrCreateChild(mCurrent - 1); Point leftOffset = subScreenSizeOffset(lv); - int gap = leftOffset.x + GAP + cvOffset.x; - lv.layout(cvLeft - lv.getMeasuredWidth() - gap, - (cvBottom + cvTop - lv.getMeasuredHeight()) / 2, cvLeft - - gap, - (cvBottom + cvTop + lv.getMeasuredHeight()) / 2); + if (HORIZONTAL_SCROLLING) { + int gap = leftOffset.x + GAP + cvOffset.x; + lv.layout(cvLeft - lv.getMeasuredWidth() - gap, (cvBottom + + cvTop - lv.getMeasuredHeight()) / 2, cvLeft - gap, + (cvBottom + cvTop + lv.getMeasuredHeight()) / 2); + } else { + int gap = leftOffset.y + GAP + cvOffset.y; + lv.layout((cvLeft + cvRight - lv.getMeasuredWidth()) / 2, cvTop + - lv.getMeasuredHeight() - gap, + (cvLeft + cvRight + lv.getMeasuredWidth()) / 2, cvTop + - gap); + } } if (mCurrent + 1 < mAdapter.getCount()) { View rv = getOrCreateChild(mCurrent + 1); Point rightOffset = subScreenSizeOffset(rv); - int gap = cvOffset.x + GAP + rightOffset.x; - rv.layout(cvRight + gap, - (cvBottom + cvTop - rv.getMeasuredHeight()) / 2, cvRight - + rv.getMeasuredWidth() + gap, - (cvBottom + cvTop + rv.getMeasuredHeight()) / 2); + if (HORIZONTAL_SCROLLING) { + int gap = cvOffset.x + GAP + rightOffset.x; + rv.layout(cvRight + gap, + (cvBottom + cvTop - rv.getMeasuredHeight()) / 2, + cvRight + rv.getMeasuredWidth() + gap, (cvBottom + + cvTop + rv.getMeasuredHeight()) / 2); + } else { + int gap = cvOffset.y + GAP + rightOffset.y; + rv.layout((cvLeft + cvRight - rv.getMeasuredWidth()) / 2, + cvBottom + gap, + (cvLeft + cvRight + rv.getMeasuredWidth()) / 2, + cvBottom + gap + rv.getMeasuredHeight()); + } } invalidate(); @@ -758,24 +794,23 @@ public Adapter getAdapter() { @Override public View getSelectedView() { - //throw new UnsupportedOperationException(getContext().getString(R.string.not_supported)); return null; } @Override public void setAdapter(Adapter adapter) { mAdapter = adapter; - //mChildViews.clear(); - //removeAllViewsInLayout(); + requestLayout(); } @Override public void setSelection(int arg0) { try { - throw new UnsupportedOperationException(getContext().getString( TiRHelper.getResource("string.not_supported") ) ); + throw new UnsupportedOperationException(getContext().getString( + TiRHelper.getResource("string.not_supported"))); } catch (ResourceNotFoundException exp) { - + } } @@ -801,7 +836,8 @@ private View getOrCreateChild(int i) { private void addAndMeasureChild(int i, View v) { LayoutParams params = v.getLayoutParams(); if (params == null) { - params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + params = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); } addViewInLayout(v, 0, params, true); mChildViews.append(i, v); // Record the view against it's adapter index @@ -917,40 +953,4 @@ private static boolean withinBoundsInDirectionOfTravel(Rect bounds, throw new NoSuchElementException(); } } - - @Override - public boolean onDoubleTap(MotionEvent e) { - float previousScale = mScale; - mScale += (mScale == 0.97f) ? 2f : -2f; - if (mScale < 2.97f) { - mScale = 0.97f; - } - if (mScale > 2.97f) { - mScale = 2.97f; - } - float factor = mScale/previousScale; - - View v = mChildViews.get(mCurrent); - if (v != null) { - // Work out the focus point relative to the view top left - int viewFocusX = (int)e.getX() - (v.getLeft() + mXScroll); - int viewFocusY = (int)e.getY() - (v.getTop() + mYScroll); - // Scroll to maintain the focus point - mXScroll += viewFocusX - viewFocusX * factor; - mYScroll += viewFocusY - viewFocusY * factor; - requestLayout(); - } - - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - return false; - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - return false; - } -} \ No newline at end of file +} diff --git a/android/src/com/artifex/mupdflib/SafeAnimatorInflater.java b/android/src/com/artifex/mupdflib/SafeAnimatorInflater.java index 0b3c55d..785987d 100644 --- a/android/src/com/artifex/mupdflib/SafeAnimatorInflater.java +++ b/android/src/com/artifex/mupdflib/SafeAnimatorInflater.java @@ -6,41 +6,37 @@ import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorSet; -import android.annotation.SuppressLint; import android.app.Activity; import android.util.Log; import android.view.View; -@SuppressLint("NewApi") -public class SafeAnimatorInflater -{ +public class SafeAnimatorInflater { private View mView; - public SafeAnimatorInflater(Activity activity, int animation, View view) - { - AnimatorSet set = null; - try{ - set = (AnimatorSet) AnimatorInflater.loadAnimator(activity, TiRHelper.getResource("anim.info")); - } catch (ResourceNotFoundException exp) { - Log.e("SageAnimatorInflater", "XML resouce not found!"); - } - mView = view; - set.setTarget(view); - set.addListener(new Animator.AnimatorListener() { - public void onAnimationStart(Animator animation) { - mView.setVisibility(View.VISIBLE); - } + public SafeAnimatorInflater(Activity activity, int animation, View view) { + try { + AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator( + activity, TiRHelper.getResource("animator.info")); + mView = view; + set.setTarget(view); + set.addListener(new Animator.AnimatorListener() { + public void onAnimationStart(Animator animation) { + mView.setVisibility(View.VISIBLE); + } - public void onAnimationRepeat(Animator animation) { - } + public void onAnimationRepeat(Animator animation) { + } - public void onAnimationEnd(Animator animation) { - mView.setVisibility(View.INVISIBLE); - } + public void onAnimationEnd(Animator animation) { + mView.setVisibility(View.INVISIBLE); + } - public void onAnimationCancel(Animator animation) { - } - }); - set.start(); + public void onAnimationCancel(Animator animation) { + } + }); + set.start(); + } catch (ResourceNotFoundException exp) { + Log.e("MuPDFPreviewGridActvity", "XML resouce not found!"); + } } } diff --git a/android/src/com/artifex/mupdflib/SafeAsyncTask.java b/android/src/com/artifex/mupdflib/SafeAsyncTask.java deleted file mode 100644 index 6706b2f..0000000 --- a/android/src/com/artifex/mupdflib/SafeAsyncTask.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.artifex.mupdflib; - -import java.util.concurrent.RejectedExecutionException; - - -public abstract class SafeAsyncTask extends AsyncTask { - public void safeExecute(Params... params) { - try { - execute(params); - } catch(RejectedExecutionException e) { - // Failed to start in the background, so do it in the foreground - onPreExecute(); - if (isCancelled()) { - onCancelled(); - } else { - onPostExecute(doInBackground(params)); - } - } - } -} diff --git a/android/src/com/artifex/mupdflib/SearchTask.java b/android/src/com/artifex/mupdflib/SearchTask.java index 8256684..fb2c59d 100644 --- a/android/src/com/artifex/mupdflib/SearchTask.java +++ b/android/src/com/artifex/mupdflib/SearchTask.java @@ -11,25 +11,43 @@ import android.content.DialogInterface; import android.graphics.RectF; import android.os.Handler; -import android.util.Log; -public abstract class SearchTask { +class ProgressDialogX extends ProgressDialog { + public ProgressDialogX(Context context) { + super(context); + } + + private boolean mCancelled = false; + + public boolean isCancelled() { + return mCancelled; + } + @Override + public void cancel() { + mCancelled = true; + super.cancel(); + } +} + +public abstract class SearchTask { private static final int SEARCH_PROGRESS_DELAY = 200; private final Context mContext; private final MuPDFCore mCore; private final Handler mHandler; - private AsyncTask mSearchTask; + private final AlertDialog.Builder mAlertBuilder; + private AsyncTask mSearchTask; public SearchTask(Context context, MuPDFCore core) { mContext = context; mCore = core; mHandler = new Handler(); + mAlertBuilder = new AlertDialog.Builder(context); } protected abstract void onTextFound(SearchTaskResult result); - protected abstract void onTextNotFound(int code); + protected abstract void onTextNotFound(int page); public void stop() { if (mSearchTask != null) { @@ -48,51 +66,58 @@ public void go(final String text, int direction, int displayPage, final int startIndex = searchPage == -1 ? displayPage : searchPage + increment; - mSearchTask = new AsyncTask() { + final ProgressDialogX progressDialog = new ProgressDialogX(mContext); + try { + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setTitle(mContext.getString(TiRHelper + .getResource("string.searching_"))); + } catch (ResourceNotFoundException exp) { + + } + progressDialog + .setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + stop(); + } + }); + progressDialog.setMax(mCore.countPages()); + + mSearchTask = new AsyncTask() { @Override - protected SearchTaskResult doInBackground(Void... params) { + protected Object doInBackground(Void... params) { int index = startIndex; while (0 <= index && index < mCore.countPages() && !isCancelled()) { publishProgress(index); - int page = index; - if (mCore.getDisplayPages() == 2) { - page = (page * 2) - 1; - } + RectF searchHits[] = mCore.searchPage(index, text); - RectF searchHits[] = mCore.searchPage(page, text); - RectF searchHitsPrim[] = mCore.getDisplayPages() == 2 ? mCore - .searchPage(page + 1, text) : null; - - if ((searchHits != null && searchHits.length > 0) - || (searchHitsPrim != null && searchHitsPrim.length > 0)) - return new SearchTaskResult(text, index, searchHits, - searchHitsPrim); + if (searchHits != null && searchHits.length > 0) + return new SearchTaskResult(text, index, searchHits); index += increment; } - return null; + return index; } @Override - protected void onPostExecute(SearchTaskResult result) { - if (result != null) { - onTextFound(result); + protected void onPostExecute(Object result) { + progressDialog.cancel(); + if (result instanceof SearchTaskResult) { + onTextFound((SearchTaskResult) result); } else { - onTextNotFound(SearchTaskResult.get() == null ? MupdfModule.ERROR_TEXT_NOT_FOUND - : MupdfModule.ERROR_NO_FURTHER_OCCURRENCES_FOUND); + onTextNotFound((Integer) result); } } @Override protected void onCancelled() { - + progressDialog.cancel(); } @Override protected void onProgressUpdate(Integer... values) { - + progressDialog.setProgress(values[0].intValue()); } @Override @@ -100,7 +125,10 @@ protected void onPreExecute() { super.onPreExecute(); mHandler.postDelayed(new Runnable() { public void run() { - + if (!progressDialog.isCancelled()) { + progressDialog.show(); + progressDialog.setProgress(startIndex); + } } }, SEARCH_PROGRESS_DELAY); } diff --git a/android/src/com/artifex/mupdflib/SearchTaskResult.java b/android/src/com/artifex/mupdflib/SearchTaskResult.java index dfc39e0..24855fd 100644 --- a/android/src/com/artifex/mupdflib/SearchTaskResult.java +++ b/android/src/com/artifex/mupdflib/SearchTaskResult.java @@ -4,16 +4,14 @@ public class SearchTaskResult { public final String txt; - public final int pageNumber; + public final int pageNumber; public final RectF searchBoxes[]; - public final RectF searchBoxesPrim[]; static private SearchTaskResult singleton; - SearchTaskResult(String _txt, int _pageNumber, RectF _searchBoxes[], RectF _searchBoxesPrim[]) { + SearchTaskResult(String _txt, int _pageNumber, RectF _searchBoxes[]) { txt = _txt; pageNumber = _pageNumber; searchBoxes = _searchBoxes; - searchBoxesPrim = _searchBoxesPrim; } static public SearchTaskResult get() { diff --git a/android/src/com/artifex/mupdflib/StorageUtils.java b/android/src/com/artifex/mupdflib/StorageUtils.java deleted file mode 100644 index cc207ed..0000000 --- a/android/src/com/artifex/mupdflib/StorageUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.artifex.mupdflib; - -import android.content.Context; -import android.os.Environment; - -import java.io.File; -import java.io.IOException; - -/** - * Provides application storage paths - * - * @author Janis Kepulis - * @since 1.0.0 - */ -public final class StorageUtils { - - private StorageUtils() { - } - - /** - * Returns application cache directory. Cache directory will be created on SD card - * ("/Android/data/[app_package_name]/cache") if card is mounted. Else - Android defines cache directory on - * device's file system. - * - * @param context Application context - * @return Cache {@link File directory} - */ - public static File getCacheDirectory(Context context) { - File appCacheDir = null; - if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { - appCacheDir = getExternalCacheDir(context); - } - if (appCacheDir == null) { - appCacheDir = context.getCacheDir(); - } - return appCacheDir; - } - - /** - * Returns application files directory. Files directory will be created on SD card - * ("/Android/data/[app_package_name]/files") if card is mounted. Else - Android defines files directory on - * device's file system. - * - * @param context Application context - * @return Files {@link File directory} - */ - public static File getFilesDirectory(Context context) { - File appFilesDir = null; - if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { - appFilesDir = getExternalFilesDir(context); - } - if (appFilesDir == null) { - appFilesDir = context.getFilesDir(); - } - return appFilesDir; - } - - public static File getCacheSubDirectory(Context context, String cacheSubDir) { - File appCacheSubDir = new File(getCacheDirectory(context),cacheSubDir); - - if (!appCacheSubDir.exists()) - appCacheSubDir.mkdirs(); - - return appCacheSubDir; - } - - public static File getFilesSubDirectory(Context context, String filesSubDir) { - File appFilesSubDir = new File(getFilesDirectory(context),filesSubDir); - - if (!appFilesSubDir.exists()) - appFilesSubDir.mkdirs(); - //String filename = String.valueOf(url.hashCode()); - // Another possible solution (thanks to grantland) - // String filename; - /* - * try { filename = URLEncoder.encode(url, "UTF-8"); } catch - * (UnsupportedEncodingException e) { - * e.printStackTrace(); } - */ - return appFilesSubDir; - } - - private static File getExternalCacheDir(Context context) { - File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); - File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache"); - if (!appCacheDir.exists()) { - if (!appCacheDir.mkdirs()) { - //L.w("Unable to create external cache directory"); - return null; - } - try { - new File(appCacheDir, ".nomedia").createNewFile(); - } catch (IOException e) { - //L.i("Can't create \".nomedia\" file in application external cache directory"); - return null; - } - } - return appCacheDir; - } - - private static File getExternalFilesDir(Context context) { - File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"); - File appFilesDir = new File(new File(dataDir, context.getPackageName()), "files"); - if (!appFilesDir.exists()) { - if (!appFilesDir.mkdirs()) { - //L.w("Unable to create external cache directory"); - return null; - } - try { - new File(appFilesDir, ".nomedia").createNewFile(); - } catch (IOException e) { - //L.i("Can't create \".nomedia\" file in application external cache directory"); - return null; - } - } - return appFilesDir; - } -} diff --git a/android/src/com/artifex/mupdflib/TextWord.java b/android/src/com/artifex/mupdflib/TextWord.java index fb993a3..58dcdab 100644 --- a/android/src/com/artifex/mupdflib/TextWord.java +++ b/android/src/com/artifex/mupdflib/TextWord.java @@ -12,6 +12,6 @@ public TextWord() { public void Add(TextChar tc) { super.union(tc); - w = w.concat(new String(new char[] { tc.c })); + w = w.concat(new String(new char[]{tc.c})); } } diff --git a/android/src/com/artifex/mupdflib/ToolbarPreviewAdapter.java b/android/src/com/artifex/mupdflib/ToolbarPreviewAdapter.java deleted file mode 100644 index d548fb7..0000000 --- a/android/src/com/artifex/mupdflib/ToolbarPreviewAdapter.java +++ /dev/null @@ -1,305 +0,0 @@ -package com.artifex.mupdflib; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.*; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.Log; -import android.util.SparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.lang.ref.WeakReference; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - -public class ToolbarPreviewAdapter extends BaseAdapter { - - private static final String TAG = ToolbarPreviewAdapter.class - .getSimpleName(); - private Context mContext; - private MuPDFCore mCore; - - private Point mPreviewSize; - private final SparseArray mBitmapCache = new SparseArray(); - private String mPath; - - private int currentlyViewing; - private Bitmap mLoadingBitmap; - - public ToolbarPreviewAdapter(Context context, MuPDFCore core) { - mContext = context; - mCore = core; - - File documentCache = new File(StorageUtils.getCacheSubDirectory( - mContext, "previews"), - MD5.MD5Hash((new File(core.getFileName())).getName())); - if (!documentCache.exists()) - documentCache.mkdirs(); - - mPath = documentCache.toString() + File.separator; - - try { - mLoadingBitmap = BitmapFactory.decodeResource( - mContext.getResources(), - TiRHelper.getResource("drawable.darkdenim3")); - } catch (ResourceNotFoundException exp) { - Log.e("ToolbarPreviewAdapter", "XML resouce not found!"); - } - } - - @Override - public int getCount() { - int count = mCore.countSinglePages(); - return count; - } - - @Override - public Object getItem(int pPosition) { - return null; - } - - @Override - public long getItemId(int pPosition) { - if (mCore.getDisplayPages() == 1) - return pPosition; - else if (pPosition > 0) - return (pPosition + 1) / 2; - else - return 0; - } - - public View getView(final int position, View convertView, ViewGroup parent) { - ViewHolder holder; - if (convertView == null) { - LayoutInflater inflater = (LayoutInflater) mContext - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - try { - convertView = inflater.inflate(TiRHelper - .getResource("layout.preview_pager_item_layout"), - parent, false); - } catch (ResourceNotFoundException exp) { - Log.e("ToolbarPreviewAdapter", "XML resouce not found!"); - } - holder = new ViewHolder(convertView); - convertView.setTag(holder); - } else { - holder = (ViewHolder) convertView.getTag(); - } - if (mPreviewSize != null) { - holder.previewPageImageView - .setLayoutParams(new LinearLayout.LayoutParams( - mPreviewSize.x, mPreviewSize.y)); - } - // holder.previewPageNumber.setText(String.valueOf(position + 1)); - holder.previewPageLinearLayout.setBackgroundColor(Color.TRANSPARENT); - drawPageImageView(holder, position); - return convertView; - } - - static class ViewHolder { - ImageView previewPageImageView = null; - // TextView previewPageNumber = null; - LinearLayout previewPageLinearLayout = null; - - ViewHolder(View view) { - try { - this.previewPageImageView = (ImageView) view - .findViewById(TiRHelper - .getResource("id.PreviewPageImageView")); - // this.previewPageNumber = (TextView) - // view.findViewById(R.id.PreviewPageNumber); - this.previewPageLinearLayout = (LinearLayout) view - .findViewById(TiRHelper - .getResource("id.PreviewPageLinearLayout")); - } catch (ResourceNotFoundException exp) { - Log.e("ToolbarPreviewAdapter", "XML resouce not found!"); - } - } - } - - private void drawPageImageView(ViewHolder holder, int position) { - if (cancelPotentialWork(holder, position)) { - final BitmapWorkerTask task = new BitmapWorkerTask(holder, position); - final AsyncDrawable asyncDrawable = new AsyncDrawable( - mContext.getResources(), mLoadingBitmap, task); - holder.previewPageImageView.setImageDrawable(asyncDrawable); - task.execute(); - } - } - - public static boolean cancelPotentialWork(ViewHolder holder, int position) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(holder.previewPageImageView); - - if (bitmapWorkerTask != null) { - final int bitmapPosition = bitmapWorkerTask.position; - if (bitmapPosition != position) { - // Cancel previous task - bitmapWorkerTask.cancel(true); - } else { - // The same work is already in progress - return false; - } - } - // No task associated with the ImageView, or an existing task was - // cancelled - return true; - } - - class BitmapWorkerTask extends AsyncTask { - - private final WeakReference viewHolderReference; - private int position; - - public BitmapWorkerTask(ViewHolder holder, int position) { - viewHolderReference = new WeakReference(holder); - this.position = position; - } - - @Override - protected Bitmap doInBackground(Integer... params) { - if (mPreviewSize == null) { - mPreviewSize = new Point(); - int padding = 0; - try { - padding = mContext - .getResources() - .getDimensionPixelSize( - TiRHelper - .getResource("dimen.toolbar_height")) - 4; - } catch (ResourceNotFoundException exp) { - Log.e("ToolbarPreviewAdapter", "XML resouce not found!"); - } - PointF mPageSize = mCore.getSinglePageSize(0); - float scale = mPageSize.y / mPageSize.x; - mPreviewSize.x = (int) ((float) padding / scale); - mPreviewSize.y = padding; - } - Bitmap lq = null; - lq = getCachedBitmap(position); - mBitmapCache.put(position, lq); - return lq; - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - if (isCancelled()) { - bitmap = null; - } - - if (viewHolderReference != null && bitmap != null) { - final ViewHolder holder = viewHolderReference.get(); - if (holder != null) { - final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(holder.previewPageImageView); - if (this == bitmapWorkerTask && holder != null) { - holder.previewPageImageView.setImageBitmap(bitmap); - // holder.previewPageNumber.setText(String.valueOf(position - // + 1)); - try { - if (getCurrentlyViewing() == position - || (mCore.getDisplayPages() == 2 && getCurrentlyViewing() == position - 1)) { - holder.previewPageLinearLayout - .setBackgroundColor(mContext - .getResources() - .getColor( - TiRHelper - .getResource("color.thumbnail_selected_background"))); - } else { - holder.previewPageLinearLayout - .setBackgroundColor(Color.TRANSPARENT); - } - } catch (ResourceNotFoundException exp) { - Log.e("ToolbarPreviewAdapter", - "XML resouce not found!"); - } - } - } - } - } - } - - private Bitmap getCachedBitmap(int position) { - String mCachedBitmapFilePath = mPath + position; - File mCachedBitmapFile = new File(mCachedBitmapFilePath); - Bitmap lq = null; - try { - if (mCachedBitmapFile.exists() && mCachedBitmapFile.canRead()) { - Log.d(TAG, "page " + position + " found in cache"); - lq = BitmapFactory.decodeFile(mCachedBitmapFilePath); - return lq; - } - } catch (Exception e) { - e.printStackTrace(); - // some error with cached file, - // delete the file and get rid of bitmap - mCachedBitmapFile.delete(); - lq = null; - } - if (lq == null) { - lq = Bitmap.createBitmap(mPreviewSize.x, mPreviewSize.y, - Bitmap.Config.ARGB_8888); - mCore.drawSinglePage(position, lq, mPreviewSize.x, mPreviewSize.y); - - // optimize bitmap format for thumbnails - Bitmap btmp = lq.copy(Bitmap.Config.RGB_565, true); - if (btmp == null) - throw new RuntimeException("bitmap copy failed"); - lq.recycle(); - lq = btmp; - - try { - lq.compress(CompressFormat.JPEG, 70, new FileOutputStream( - mCachedBitmapFile)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - mCachedBitmapFile.delete(); - } - } - return lq; - } - - public int getCurrentlyViewing() { - return currentlyViewing; - } - - public void setCurrentlyViewing(int currentlyViewing) { - this.currentlyViewing = currentlyViewing; - notifyDataSetChanged(); - } - - static class AsyncDrawable extends BitmapDrawable { - private final WeakReference bitmapWorkerTaskReference; - - public AsyncDrawable(Resources res, Bitmap bitmap, - BitmapWorkerTask bitmapWorkerTask) { - super(res, bitmap); - bitmapWorkerTaskReference = new WeakReference( - bitmapWorkerTask); - } - - public BitmapWorkerTask getBitmapWorkerTask() { - return bitmapWorkerTaskReference.get(); - } - } - - private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { - if (imageView != null) { - final Drawable drawable = imageView.getDrawable(); - if (drawable instanceof AsyncDrawable) { - final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; - return asyncDrawable.getBitmapWorkerTask(); - } - } - return null; - } -} diff --git a/android/src/com/artifex/mupdflib/TwoWayView.java b/android/src/com/artifex/mupdflib/TwoWayView.java deleted file mode 100644 index 4c8fc4c..0000000 --- a/android/src/com/artifex/mupdflib/TwoWayView.java +++ /dev/null @@ -1,6835 +0,0 @@ -/* - * Copyright (C) 2013 Lucas Rocha - * - * This code is based on bits and pieces of Android's AbsListView, - * Listview, and StaggeredGridView. - * - * Copyright (C) 2012 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.artifex.mupdflib; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.TypedArray; -import android.database.DataSetObserver; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.TransitionDrawable; -import android.os.*; -import android.support.v4.util.LongSparseArray; -import android.support.v4.util.SparseArrayCompat; -import android.support.v4.view.*; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; -import android.support.v4.widget.EdgeEffectCompat; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseBooleanArray; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.*; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.AdapterView; -import android.widget.Checkable; -import android.widget.ListAdapter; -import android.widget.Scroller; - -import java.util.ArrayList; -import java.util.List; - -import org.appcelerator.titanium.util.TiRHelper; -import org.appcelerator.titanium.util.TiRHelper.ResourceNotFoundException; - -/* - * Implementation Notes: - * - * Some terminology: - * - * index - index of the items that are currently visible - * position - index of the items in the cursor - * - * Given the bi-directional nature of this view, the source code - * usually names variables with 'start' to mean 'top' or 'left'; and - * 'end' to mean 'bottom' or 'right', depending on the current - * orientation of the widget. - */ - -/** - * A view that shows items in a vertical or horizontal scrolling list. The items - * come from the {@link ListAdapter} associated with this view. - */ -public class TwoWayView extends AdapterView implements - ViewTreeObserver.OnTouchModeChangeListener { - private static final String LOGTAG = "TwoWayView"; - - private static final int NO_POSITION = -1; - private static final int INVALID_POINTER = -1; - - public static final int[] STATE_NOTHING = new int[] { 0 }; - - private static final int TOUCH_MODE_REST = -1; - private static final int TOUCH_MODE_DOWN = 0; - private static final int TOUCH_MODE_TAP = 1; - private static final int TOUCH_MODE_DONE_WAITING = 2; - private static final int TOUCH_MODE_DRAGGING = 3; - private static final int TOUCH_MODE_FLINGING = 4; - private static final int TOUCH_MODE_OVERSCROLL = 5; - - private static final int TOUCH_MODE_UNKNOWN = -1; - private static final int TOUCH_MODE_ON = 0; - private static final int TOUCH_MODE_OFF = 1; - - private static final int LAYOUT_NORMAL = 0; - private static final int LAYOUT_FORCE_TOP = 1; - private static final int LAYOUT_SET_SELECTION = 2; - private static final int LAYOUT_FORCE_BOTTOM = 3; - private static final int LAYOUT_SPECIFIC = 4; - private static final int LAYOUT_SYNC = 5; - private static final int LAYOUT_MOVE_SELECTION = 6; - - private static final int SYNC_SELECTED_POSITION = 0; - private static final int SYNC_FIRST_POSITION = 1; - - private static final int SYNC_MAX_DURATION_MILLIS = 100; - - private static final int CHECK_POSITION_SEARCH_DISTANCE = 20; - - private static final float MAX_SCROLL_FACTOR = 0.33f; - - private static final int MIN_SCROLL_PREVIEW_PIXELS = 10; - - public static enum ChoiceMode { - NONE, SINGLE, MULTIPLE - } - - public static enum Orientation { - HORIZONTAL, VERTICAL; - }; - - private ListAdapter mAdapter; - - private boolean mIsVertical; - - private int mItemMargin; - - private boolean mInLayout; - private boolean mBlockLayoutRequests; - - private boolean mIsAttached; - - private final RecycleBin mRecycler; - private AdapterDataSetObserver mDataSetObserver; - - private boolean mItemsCanFocus; - - final boolean[] mIsScrap = new boolean[1]; - - private boolean mDataChanged; - private int mItemCount; - private int mOldItemCount; - private boolean mHasStableIds; - private boolean mAreAllItemsSelectable; - - private int mFirstPosition; - private int mSpecificStart; - - private SavedState mPendingSync; - - private final int mTouchSlop; - private final int mMaximumVelocity; - private final int mFlingVelocity; - private float mLastTouchPos; - private float mTouchRemainderPos; - private int mActivePointerId; - - private final Rect mTempRect; - - private final ArrowScrollFocusResult mArrowScrollFocusResult; - - private Rect mTouchFrame; - private int mMotionPosition; - private CheckForTap mPendingCheckForTap; - private CheckForLongPress mPendingCheckForLongPress; - private CheckForKeyLongPress mPendingCheckForKeyLongPress; - private PerformClick mPerformClick; - private Runnable mTouchModeReset; - private int mResurrectToPosition; - - private boolean mIsChildViewEnabled; - - private boolean mDrawSelectorOnTop; - private Drawable mSelector; - private int mSelectorPosition; - private final Rect mSelectorRect; - - private int mOverScroll; - private final int mOverscrollDistance; - - private boolean mDesiredFocusableState; - private boolean mDesiredFocusableInTouchModeState; - - private SelectionNotifier mSelectionNotifier; - - private boolean mNeedSync; - private int mSyncMode; - private int mSyncPosition; - private long mSyncRowId; - private long mSyncHeight; - private int mSelectedStart; - - private int mNextSelectedPosition; - private long mNextSelectedRowId; - private int mSelectedPosition; - private long mSelectedRowId; - private int mOldSelectedPosition; - private long mOldSelectedRowId; - - private ChoiceMode mChoiceMode; - private int mCheckedItemCount; - private SparseBooleanArray mCheckStates; - LongSparseArray mCheckedIdStates; - - private ContextMenuInfo mContextMenuInfo; - - private int mLayoutMode; - private int mTouchMode; - private int mLastTouchMode; - private VelocityTracker mVelocityTracker; - private final Scroller mScroller; - - private EdgeEffectCompat mStartEdge; - private EdgeEffectCompat mEndEdge; - - private OnScrollListener mOnScrollListener; - private int mLastScrollState; - - private View mEmptyView; - - private ListItemAccessibilityDelegate mAccessibilityDelegate; - - private int mLastAccessibilityScrollEventFromIndex; - private int mLastAccessibilityScrollEventToIndex; - - public interface OnScrollListener { - - /** - * The view is not scrolling. Note navigating the list using the - * trackball counts as being in the idle state since these transitions - * are not animated. - */ - public static int SCROLL_STATE_IDLE = 0; - - /** - * The user is scrolling using touch, and their finger is still on the - * screen - */ - public static int SCROLL_STATE_TOUCH_SCROLL = 1; - - /** - * The user had previously been scrolling using touch and had performed - * a fling. The animation is now coasting to a stop - */ - public static int SCROLL_STATE_FLING = 2; - - /** - * Callback method to be invoked while the list view or grid view is - * being scrolled. If the view is being scrolled, this method will be - * called before the next frame of the scroll is rendered. In - * particular, it will be called before any calls to - * {@link Adapter#getView(int, View, ViewGroup)}. - * - * @param view - * The view whose scroll state is being reported - * - * @param scrollState - * The current scroll state. One of - * {@link #SCROLL_STATE_IDLE}, - * {@link #SCROLL_STATE_TOUCH_SCROLL} or - * {@link #SCROLL_STATE_IDLE}. - */ - public void onScrollStateChanged(TwoWayView view, int scrollState); - - /** - * Callback method to be invoked when the list or grid has been - * scrolled. This will be called after the scroll has completed - * - * @param view - * The view whose scroll state is being reported - * @param firstVisibleItem - * the index of the first visible cell (ignore if - * visibleItemCount == 0) - * @param visibleItemCount - * the number of visible cells - * @param totalItemCount - * the number of items in the list adaptor - */ - public void onScroll(TwoWayView view, int firstVisibleItem, - int visibleItemCount, int totalItemCount); - } - - /** - * A RecyclerListener is used to receive a notification whenever a View is - * placed inside the RecycleBin's scrap heap. This listener is used to free - * resources associated to Views placed in the RecycleBin. - * - * @see TwoWayView.RecycleBin - * @see TwoWayView#setRecyclerListener(TwoWayView.RecyclerListener) - */ - public static interface RecyclerListener { - /** - * Indicates that the specified View was moved into the recycler's scrap - * heap. The view is not displayed on screen any more and any expensive - * resource associated with the view should be discarded. - * - * @param view - */ - void onMovedToScrapHeap(View view); - } - - public TwoWayView(Context context) { - this(context, null); - } - - public TwoWayView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public TwoWayView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mNeedSync = false; - mVelocityTracker = null; - - mLayoutMode = LAYOUT_NORMAL; - mTouchMode = TOUCH_MODE_REST; - mLastTouchMode = TOUCH_MODE_UNKNOWN; - - mIsAttached = false; - - mContextMenuInfo = null; - - mOnScrollListener = null; - mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE; - - final ViewConfiguration vc = ViewConfiguration.get(context); - mTouchSlop = vc.getScaledTouchSlop(); - mMaximumVelocity = vc.getScaledMaximumFlingVelocity(); - mFlingVelocity = vc.getScaledMinimumFlingVelocity(); - mOverscrollDistance = getScaledOverscrollDistance(vc); - - mOverScroll = 0; - - mScroller = new Scroller(context); - - mIsVertical = true; - - mItemsCanFocus = false; - - mTempRect = new Rect(); - - mArrowScrollFocusResult = new ArrowScrollFocusResult(); - - mSelectorPosition = INVALID_POSITION; - - mSelectorRect = new Rect(); - mSelectedStart = 0; - - mResurrectToPosition = INVALID_POSITION; - - mSelectedStart = 0; - mNextSelectedPosition = INVALID_POSITION; - mNextSelectedRowId = INVALID_ROW_ID; - mSelectedPosition = INVALID_POSITION; - mSelectedRowId = INVALID_ROW_ID; - mOldSelectedPosition = INVALID_POSITION; - mOldSelectedRowId = INVALID_ROW_ID; - - mChoiceMode = ChoiceMode.NONE; - mCheckedItemCount = 0; - mCheckedIdStates = null; - mCheckStates = null; - - mRecycler = new RecycleBin(); - mDataSetObserver = null; - - mAreAllItemsSelectable = true; - - mStartEdge = null; - mEndEdge = null; - - setClickable(true); - setFocusableInTouchMode(true); - setWillNotDraw(false); - setAlwaysDrawnWithCacheEnabled(false); - setWillNotDraw(false); - setClipToPadding(false); - - ViewCompat.setOverScrollMode(this, - ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS); - - try { - int[] styles = new int[] { TiRHelper - .getResource("styleable.TwoWayView") }; - TypedArray a = context.obtainStyledAttributes(attrs, styles, - defStyle, 0); - initializeScrollbars(a); - - mDrawSelectorOnTop = a - .getBoolean( - TiRHelper - .getResource("styleable.TwoWayView_android_drawSelectorOnTop"), - false); - - Drawable d = a.getDrawable(TiRHelper - .getResource("styleable.TwoWayView_android_listSelector")); - if (d != null) { - setSelector(d); - } - - int orientation = a.getInt(TiRHelper - .getResource("styleable.TwoWayView_android_orientation"), - -1); - if (orientation >= 0) { - setOrientation(Orientation.values()[orientation]); - } - - int choiceMode = a - .getInt(TiRHelper - .getResource("styleable.TwoWayView_android_choiceMode"), - -1); - if (choiceMode >= 0) { - setChoiceMode(ChoiceMode.values()[choiceMode]); - } - a.recycle(); - } catch (ResourceNotFoundException exp) { - Log.e("TwoWayView", "XML resouce not found!"); - } - - updateScrollbarsDirection(); - } - - public void setOrientation(Orientation orientation) { - final boolean isVertical = (orientation.compareTo(Orientation.VERTICAL) == 0); - if (mIsVertical == isVertical) { - return; - } - - mIsVertical = isVertical; - - updateScrollbarsDirection(); - resetState(); - mRecycler.clear(); - - requestLayout(); - } - - public Orientation getOrientation() { - return (mIsVertical ? Orientation.VERTICAL : Orientation.HORIZONTAL); - } - - public void setItemMargin(int itemMargin) { - if (mItemMargin == itemMargin) { - return; - } - - mItemMargin = itemMargin; - requestLayout(); - } - - public int getItemMargin() { - return mItemMargin; - } - - /** - * Indicates that the views created by the ListAdapter can contain focusable - * items. - * - * @param itemsCanFocus - * true if items can get focus, false otherwise - */ - public void setItemsCanFocus(boolean itemsCanFocus) { - mItemsCanFocus = itemsCanFocus; - if (!itemsCanFocus) { - setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - } - } - - /** - * @return Whether the views created by the ListAdapter can contain - * focusable items. - */ - public boolean getItemsCanFocus() { - return mItemsCanFocus; - } - - /** - * Set the listener that will receive notifications every time the list - * scrolls. - * - * @param l - * the scroll listener - */ - public void setOnScrollListener(OnScrollListener l) { - mOnScrollListener = l; - invokeOnItemScrollListener(); - } - - /** - * Sets the recycler listener to be notified whenever a View is set aside in - * the recycler for later reuse. This listener can be used to free resources - * associated to the View. - * - * @param listener - * The recycler listener to be notified of views set aside in the - * recycler. - * - * @see TwoWayView.RecycleBin - * @see TwoWayView.RecyclerListener - */ - public void setRecyclerListener(RecyclerListener l) { - mRecycler.mRecyclerListener = l; - } - - /** - * Controls whether the selection highlight drawable should be drawn on top - * of the item or behind it. - * - * @param onTop - * If true, the selector will be drawn on the item it is - * highlighting. The default is false. - * - * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop - */ - public void setDrawSelectorOnTop(boolean drawSelectorOnTop) { - mDrawSelectorOnTop = drawSelectorOnTop; - } - - /** - * Set a Drawable that should be used to highlight the currently selected - * item. - * - * @param resID - * A Drawable resource to use as the selection highlight. - * - * @attr ref android.R.styleable#AbsListView_listSelector - */ - public void setSelector(int resID) { - setSelector(getResources().getDrawable(resID)); - } - - /** - * Set a Drawable that should be used to highlight the currently selected - * item. - * - * @param selector - * A Drawable to use as the selection highlight. - * - * @attr ref android.R.styleable#AbsListView_listSelector - */ - public void setSelector(Drawable selector) { - if (mSelector != null) { - mSelector.setCallback(null); - unscheduleDrawable(mSelector); - } - - mSelector = selector; - Rect padding = new Rect(); - selector.getPadding(padding); - - selector.setCallback(this); - updateSelectorState(); - } - - /** - * Returns the selector {@link android.graphics.drawable.Drawable} that is - * used to draw the selection in the list. - * - * @return the drawable used to display the selector - */ - public Drawable getSelector() { - return mSelector; - } - - /** - * {@inheritDoc} - */ - @Override - public int getSelectedItemPosition() { - return mNextSelectedPosition; - } - - /** - * {@inheritDoc} - */ - @Override - public long getSelectedItemId() { - return mNextSelectedRowId; - } - - /** - * Returns the number of items currently selected. This will only be valid - * if the choice mode is not {@link #CHOICE_MODE_NONE} (default). - * - *

- * To determine the specific items that are currently selected, use one of - * the getChecked* methods. - * - * @return The number of items currently selected - * - * @see #getCheckedItemPosition() - * @see #getCheckedItemPositions() - * @see #getCheckedItemIds() - */ - public int getCheckedItemCount() { - return mCheckedItemCount; - } - - /** - * Returns the checked state of the specified position. The result is only - * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or - * {@link #CHOICE_MODE_MULTIPLE}. - * - * @param position - * The item whose checked state to return - * @return The item's checked state or false if choice mode is - * invalid - * - * @see #setChoiceMode(int) - */ - public boolean isItemChecked(int position) { - if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0 && mCheckStates != null) { - return mCheckStates.get(position); - } - - return false; - } - - /** - * Returns the currently checked item. The result is only valid if the - * choice mode has been set to {@link #CHOICE_MODE_SINGLE}. - * - * @return The position of the currently checked item or - * {@link #INVALID_POSITION} if nothing is selected - * - * @see #setChoiceMode(int) - */ - public int getCheckedItemPosition() { - if (mChoiceMode.compareTo(ChoiceMode.SINGLE) == 0 - && mCheckStates != null && mCheckStates.size() == 1) { - return mCheckStates.keyAt(0); - } - - return INVALID_POSITION; - } - - /** - * Returns the set of checked items in the list. The result is only valid if - * the choice mode has not been set to {@link #CHOICE_MODE_NONE}. - * - * @return A SparseBooleanArray which will return true for each call to - * get(int position) where position is a position in the list, or - * null if the choice mode is set to - * {@link #CHOICE_MODE_NONE}. - */ - public SparseBooleanArray getCheckedItemPositions() { - if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0) { - return mCheckStates; - } - - return null; - } - - /** - * Returns the set of checked items ids. The result is only valid if the - * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter - * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true}) - * - * @return A new array which contains the id of each checked item in the - * list. - */ - public long[] getCheckedItemIds() { - if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0 - || mCheckedIdStates == null || mAdapter == null) { - return new long[0]; - } - - final LongSparseArray idStates = mCheckedIdStates; - final int count = idStates.size(); - final long[] ids = new long[count]; - - for (int i = 0; i < count; i++) { - ids[i] = idStates.keyAt(i); - } - - return ids; - } - - /** - * Sets the checked state of the specified position. The is only valid if - * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or - * {@link #CHOICE_MODE_MULTIPLE}. - * - * @param position - * The item whose checked state is to be checked - * @param value - * The new checked state for the item - */ - public void setItemChecked(int position, boolean value) { - if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0) { - return; - } - - if (mChoiceMode.compareTo(ChoiceMode.MULTIPLE) == 0) { - boolean oldValue = mCheckStates.get(position); - mCheckStates.put(position, value); - - if (mCheckedIdStates != null && mAdapter.hasStableIds()) { - if (value) { - mCheckedIdStates - .put(mAdapter.getItemId(position), position); - } else { - mCheckedIdStates.delete(mAdapter.getItemId(position)); - } - } - - if (oldValue != value) { - if (value) { - mCheckedItemCount++; - } else { - mCheckedItemCount--; - } - } - } else { - boolean updateIds = mCheckedIdStates != null - && mAdapter.hasStableIds(); - - // Clear all values if we're checking something, or unchecking the - // currently - // selected item - if (value || isItemChecked(position)) { - mCheckStates.clear(); - - if (updateIds) { - mCheckedIdStates.clear(); - } - } - - // This may end up selecting the value we just cleared but this way - // we ensure length of mCheckStates is 1, a fact - // getCheckedItemPosition relies on - if (value) { - mCheckStates.put(position, true); - - if (updateIds) { - mCheckedIdStates - .put(mAdapter.getItemId(position), position); - } - - mCheckedItemCount = 1; - } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { - mCheckedItemCount = 0; - } - } - - // Do not generate a data change while we are in the layout phase - if (!mInLayout && !mBlockLayoutRequests) { - mDataChanged = true; - rememberSyncState(); - requestLayout(); - } - } - - /** - * Clear any choices previously set - */ - public void clearChoices() { - if (mCheckStates != null) { - mCheckStates.clear(); - } - - if (mCheckedIdStates != null) { - mCheckedIdStates.clear(); - } - - mCheckedItemCount = 0; - } - - /** - * @see #setChoiceMode(int) - * - * @return The current choice mode - */ - public ChoiceMode getChoiceMode() { - return mChoiceMode; - } - - /** - * Defines the choice behavior for the List. By default, Lists do not have - * any choice behavior ({@link #CHOICE_MODE_NONE}). By setting the - * choiceMode to {@link #CHOICE_MODE_SINGLE}, the List allows up to one item - * to be in a chosen state. By setting the choiceMode to - * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be - * chosen. - * - * @param choiceMode - * One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, - * or {@link #CHOICE_MODE_MULTIPLE} - */ - public void setChoiceMode(ChoiceMode choiceMode) { - mChoiceMode = choiceMode; - - if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0) { - if (mCheckStates == null) { - mCheckStates = new SparseBooleanArray(); - } - - if (mCheckedIdStates == null && mAdapter != null - && mAdapter.hasStableIds()) { - mCheckedIdStates = new LongSparseArray(); - } - } - } - - @Override - public ListAdapter getAdapter() { - return mAdapter; - } - - @Override - public void setAdapter(ListAdapter adapter) { - if (mAdapter != null && mDataSetObserver != null) { - mAdapter.unregisterDataSetObserver(mDataSetObserver); - } - - resetState(); - mRecycler.clear(); - - mAdapter = adapter; - mDataChanged = true; - - mOldSelectedPosition = INVALID_POSITION; - mOldSelectedRowId = INVALID_ROW_ID; - - if (mCheckStates != null) { - mCheckStates.clear(); - } - - if (mCheckedIdStates != null) { - mCheckedIdStates.clear(); - } - - if (mAdapter != null) { - mOldItemCount = mItemCount; - mItemCount = adapter.getCount(); - - mDataSetObserver = new AdapterDataSetObserver(); - mAdapter.registerDataSetObserver(mDataSetObserver); - - mRecycler.setViewTypeCount(adapter.getViewTypeCount()); - - mHasStableIds = adapter.hasStableIds(); - mAreAllItemsSelectable = adapter.areAllItemsEnabled(); - - if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mHasStableIds - && mCheckedIdStates == null) { - mCheckedIdStates = new LongSparseArray(); - } - - final int position = lookForSelectablePosition(0); - setSelectedPositionInt(position); - setNextSelectedPositionInt(position); - - if (mItemCount == 0) { - checkSelectionChanged(); - } - } else { - mItemCount = 0; - mHasStableIds = false; - mAreAllItemsSelectable = true; - - checkSelectionChanged(); - } - - checkFocus(); - requestLayout(); - } - - @Override - public int getFirstVisiblePosition() { - return mFirstPosition; - } - - @Override - public int getLastVisiblePosition() { - return mFirstPosition + getChildCount() - 1; - } - - @Override - public int getCount() { - return mItemCount; - } - - @Override - public int getPositionForView(View view) { - View child = view; - try { - View v; - while (!(v = (View) child.getParent()).equals(this)) { - child = v; - } - } catch (ClassCastException e) { - // We made it up to the window without find this list view - return INVALID_POSITION; - } - - // Search the children for the list item - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - if (getChildAt(i).equals(child)) { - return mFirstPosition + i; - } - } - - // Child not found! - return INVALID_POSITION; - } - - @Override - public void getFocusedRect(Rect r) { - View view = getSelectedView(); - - if (view != null && view.getParent() == this) { - // The focused rectangle of the selected view offset into the - // coordinate space of this view. - view.getFocusedRect(r); - offsetDescendantRectToMyCoords(view, r); - } else { - super.getFocusedRect(r); - } - } - - @Override - protected void onFocusChanged(boolean gainFocus, int direction, - Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - - if (gainFocus && mSelectedPosition < 0 && !isInTouchMode()) { - if (!mIsAttached && mAdapter != null) { - // Data may have changed while we were detached and it's valid - // to change focus while detached. Refresh so we don't die. - mDataChanged = true; - mOldItemCount = mItemCount; - mItemCount = mAdapter.getCount(); - } - - resurrectSelection(); - } - - final ListAdapter adapter = mAdapter; - int closetChildIndex = INVALID_POSITION; - int closestChildStart = 0; - - if (adapter != null && gainFocus && previouslyFocusedRect != null) { - previouslyFocusedRect.offset(getScrollX(), getScrollY()); - - // Don't cache the result of getChildCount or mFirstPosition here, - // it could change in layoutChildren. - if (adapter.getCount() < getChildCount() + mFirstPosition) { - mLayoutMode = LAYOUT_NORMAL; - layoutChildren(); - } - - // Figure out which item should be selected based on previously - // focused rect. - Rect otherRect = mTempRect; - int minDistance = Integer.MAX_VALUE; - final int childCount = getChildCount(); - final int firstPosition = mFirstPosition; - - for (int i = 0; i < childCount; i++) { - // Only consider selectable views - if (!adapter.isEnabled(firstPosition + i)) { - continue; - } - - View other = getChildAt(i); - other.getDrawingRect(otherRect); - offsetDescendantRectToMyCoords(other, otherRect); - int distance = getDistance(previouslyFocusedRect, otherRect, - direction); - - if (distance < minDistance) { - minDistance = distance; - closetChildIndex = i; - closestChildStart = (mIsVertical ? other.getTop() : other - .getLeft()); - } - } - } - - if (closetChildIndex >= 0) { - setSelectionFromOffset(closetChildIndex + mFirstPosition, - closestChildStart); - } else { - requestLayout(); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - final ViewTreeObserver treeObserver = getViewTreeObserver(); - treeObserver.addOnTouchModeChangeListener(this); - - if (mAdapter != null && mDataSetObserver == null) { - mDataSetObserver = new AdapterDataSetObserver(); - mAdapter.registerDataSetObserver(mDataSetObserver); - - // Data may have changed while we were detached. Refresh. - mDataChanged = true; - mOldItemCount = mItemCount; - mItemCount = mAdapter.getCount(); - } - - mIsAttached = true; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - // Detach any view left in the scrap heap - mRecycler.clear(); - - final ViewTreeObserver treeObserver = getViewTreeObserver(); - treeObserver.removeOnTouchModeChangeListener(this); - - if (mAdapter != null) { - mAdapter.unregisterDataSetObserver(mDataSetObserver); - mDataSetObserver = null; - } - - if (mPerformClick != null) { - removeCallbacks(mPerformClick); - } - - if (mTouchModeReset != null) { - removeCallbacks(mTouchModeReset); - mTouchModeReset.run(); - } - - mIsAttached = false; - } - - @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - - final int touchMode = isInTouchMode() ? TOUCH_MODE_ON : TOUCH_MODE_OFF; - - if (!hasWindowFocus) { - if (touchMode == TOUCH_MODE_OFF) { - // Remember the last selected element - mResurrectToPosition = mSelectedPosition; - } - } else { - // If we changed touch mode since the last time we had focus - if (touchMode != mLastTouchMode - && mLastTouchMode != TOUCH_MODE_UNKNOWN) { - // If we come back in trackball mode, we bring the selection - // back - if (touchMode == TOUCH_MODE_OFF) { - // This will trigger a layout - resurrectSelection(); - - // If we come back in touch mode, then we want to hide the - // selector - } else { - hideSelector(); - mLayoutMode = LAYOUT_NORMAL; - layoutChildren(); - } - } - } - - mLastTouchMode = touchMode; - } - - @Override - protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY) { - boolean needsInvalidate = false; - - if (mIsVertical && mOverScroll != scrollY) { - onScrollChanged(getScrollX(), scrollY, getScrollX(), mOverScroll); - mOverScroll = scrollY; - needsInvalidate = true; - } else if (!mIsVertical && mOverScroll != scrollX) { - onScrollChanged(scrollX, getScrollY(), mOverScroll, getScrollY()); - mOverScroll = scrollX; - needsInvalidate = true; - } - - if (needsInvalidate) { - invalidate(); - awakenScrollbarsInternal(); - } - } - - @TargetApi(9) - private boolean overScrollByInternal(int deltaX, int deltaY, int scrollX, - int scrollY, int scrollRangeX, int scrollRangeY, - int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { - if (Build.VERSION.SDK_INT < 9) { - return false; - } - - return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, - scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, - isTouchEvent); - } - - @Override - @TargetApi(9) - public void setOverScrollMode(int mode) { - if (Build.VERSION.SDK_INT < 9) { - return; - } - - if (mode != ViewCompat.OVER_SCROLL_NEVER) { - if (mStartEdge == null) { - Context context = getContext(); - - mStartEdge = new EdgeEffectCompat(context); - mEndEdge = new EdgeEffectCompat(context); - } - } else { - mStartEdge = null; - mEndEdge = null; - } - - super.setOverScrollMode(mode); - } - - public int pointToPosition(int x, int y) { - Rect frame = mTouchFrame; - if (frame == null) { - mTouchFrame = new Rect(); - frame = mTouchFrame; - } - - final int count = getChildCount(); - for (int i = count - 1; i >= 0; i--) { - final View child = getChildAt(i); - - if (child.getVisibility() == View.VISIBLE) { - child.getHitRect(frame); - - if (frame.contains(x, y)) { - return mFirstPosition + i; - } - } - } - return INVALID_POSITION; - } - - @Override - protected int computeVerticalScrollExtent() { - final int count = getChildCount(); - if (count == 0) { - return 0; - } - - int extent = count * 100; - - View child = getChildAt(0); - final int childTop = child.getTop(); - - int childHeight = child.getHeight(); - if (childHeight > 0) { - extent += (childTop * 100) / childHeight; - } - - child = getChildAt(count - 1); - final int childBottom = child.getBottom(); - - childHeight = child.getHeight(); - if (childHeight > 0) { - extent -= ((childBottom - getHeight()) * 100) / childHeight; - } - - return extent; - } - - @Override - protected int computeHorizontalScrollExtent() { - final int count = getChildCount(); - if (count == 0) { - return 0; - } - - int extent = count * 100; - - View child = getChildAt(0); - final int childLeft = child.getLeft(); - - int childWidth = child.getWidth(); - if (childWidth > 0) { - extent += (childLeft * 100) / childWidth; - } - - child = getChildAt(count - 1); - final int childRight = child.getRight(); - - childWidth = child.getWidth(); - if (childWidth > 0) { - extent -= ((childRight - getWidth()) * 100) / childWidth; - } - - return extent; - } - - @Override - protected int computeVerticalScrollOffset() { - final int firstPosition = mFirstPosition; - final int childCount = getChildCount(); - - if (firstPosition < 0 || childCount == 0) { - return 0; - } - - final View child = getChildAt(0); - final int childTop = child.getTop(); - - int childHeight = child.getHeight(); - if (childHeight > 0) { - return Math.max(firstPosition * 100 - (childTop * 100) - / childHeight, 0); - } - - return 0; - } - - @Override - protected int computeHorizontalScrollOffset() { - final int firstPosition = mFirstPosition; - final int childCount = getChildCount(); - - if (firstPosition < 0 || childCount == 0) { - return 0; - } - - final View child = getChildAt(0); - final int childLeft = child.getLeft(); - - int childWidth = child.getWidth(); - if (childWidth > 0) { - return Math.max(firstPosition * 100 - (childLeft * 100) - / childWidth, 0); - } - - return 0; - } - - @Override - protected int computeVerticalScrollRange() { - int result = Math.max(mItemCount * 100, 0); - - if (mIsVertical && mOverScroll != 0) { - // Compensate for overscroll - result += Math.abs((int) ((float) mOverScroll / getHeight() - * mItemCount * 100)); - } - - return result; - } - - @Override - protected int computeHorizontalScrollRange() { - int result = Math.max(mItemCount * 100, 0); - - if (!mIsVertical && mOverScroll != 0) { - // Compensate for overscroll - result += Math.abs((int) ((float) mOverScroll / getWidth() - * mItemCount * 100)); - } - - return result; - } - - @Override - public boolean showContextMenuForChild(View originalView) { - final int longPressPosition = getPositionForView(originalView); - if (longPressPosition >= 0) { - final long longPressId = mAdapter.getItemId(longPressPosition); - boolean handled = false; - - OnItemLongClickListener listener = getOnItemLongClickListener(); - if (listener != null) { - handled = listener.onItemLongClick(TwoWayView.this, - originalView, longPressPosition, longPressId); - } - - if (!handled) { - mContextMenuInfo = createContextMenuInfo( - getChildAt(longPressPosition - mFirstPosition), - longPressPosition, longPressId); - - handled = super.showContextMenuForChild(originalView); - } - - return handled; - } - - return false; - } - - @Override - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - if (disallowIntercept) { - recycleVelocityTracker(); - } - - super.requestDisallowInterceptTouchEvent(disallowIntercept); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (!mIsAttached) { - return false; - } - - final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; - switch (action) { - case MotionEvent.ACTION_DOWN: - initOrResetVelocityTracker(); - mVelocityTracker.addMovement(ev); - - mScroller.abortAnimation(); - - final float x = ev.getX(); - final float y = ev.getY(); - - mLastTouchPos = (mIsVertical ? y : x); - - final int motionPosition = findMotionRowOrColumn((int) mLastTouchPos); - - mActivePointerId = MotionEventCompat.getPointerId(ev, 0); - mTouchRemainderPos = 0; - - if (mTouchMode == TOUCH_MODE_FLINGING) { - return true; - } else if (motionPosition >= 0) { - mMotionPosition = motionPosition; - mTouchMode = TOUCH_MODE_DOWN; - } - - break; - - case MotionEvent.ACTION_MOVE: { - if (mTouchMode != TOUCH_MODE_DOWN) { - break; - } - - initVelocityTrackerIfNotExists(); - mVelocityTracker.addMovement(ev); - - final int index = MotionEventCompat.findPointerIndex(ev, - mActivePointerId); - if (index < 0) { - Log.e(LOGTAG, - "onInterceptTouchEvent could not find pointer with id " - + mActivePointerId - + " - did TwoWayView receive an inconsistent " - + "event stream?"); - return false; - } - - final float pos; - if (mIsVertical) { - pos = MotionEventCompat.getY(ev, index); - } else { - pos = MotionEventCompat.getX(ev, index); - } - - final float diff = pos - mLastTouchPos + mTouchRemainderPos; - final int delta = (int) diff; - mTouchRemainderPos = diff - delta; - - if (maybeStartScrolling(delta)) { - return true; - } - - break; - } - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - mActivePointerId = INVALID_POINTER; - mTouchMode = TOUCH_MODE_REST; - recycleVelocityTracker(); - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - - break; - } - - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (!isEnabled()) { - // A disabled view that is clickable still consumes the touch - // events, it just doesn't respond to them. - return isClickable() || isLongClickable(); - } - - if (!mIsAttached) { - return false; - } - - boolean needsInvalidate = false; - - initVelocityTrackerIfNotExists(); - mVelocityTracker.addMovement(ev); - - final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; - switch (action) { - case MotionEvent.ACTION_DOWN: { - if (mDataChanged) { - break; - } - - mVelocityTracker.clear(); - mScroller.abortAnimation(); - - final float x = ev.getX(); - final float y = ev.getY(); - - mLastTouchPos = (mIsVertical ? y : x); - - int motionPosition = pointToPosition((int) x, (int) y); - - mActivePointerId = MotionEventCompat.getPointerId(ev, 0); - mTouchRemainderPos = 0; - - if (mDataChanged) { - break; - } - - if (mTouchMode == TOUCH_MODE_FLINGING) { - mTouchMode = TOUCH_MODE_DRAGGING; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); - motionPosition = findMotionRowOrColumn((int) mLastTouchPos); - return true; - } else if (mMotionPosition >= 0 - && mAdapter.isEnabled(mMotionPosition)) { - mTouchMode = TOUCH_MODE_DOWN; - triggerCheckForTap(); - } - - mMotionPosition = motionPosition; - - break; - } - - case MotionEvent.ACTION_MOVE: { - final int index = MotionEventCompat.findPointerIndex(ev, - mActivePointerId); - if (index < 0) { - Log.e(LOGTAG, - "onInterceptTouchEvent could not find pointer with id " - + mActivePointerId - + " - did TwoWayView receive an inconsistent " - + "event stream?"); - return false; - } - - final float pos; - if (mIsVertical) { - pos = MotionEventCompat.getY(ev, index); - } else { - pos = MotionEventCompat.getX(ev, index); - } - - if (mDataChanged) { - // Re-sync everything if data has been changed - // since the scroll operation can query the adapter. - layoutChildren(); - } - - final float diff = pos - mLastTouchPos + mTouchRemainderPos; - final int delta = (int) diff; - mTouchRemainderPos = diff - delta; - - switch (mTouchMode) { - case TOUCH_MODE_DOWN: - case TOUCH_MODE_TAP: - case TOUCH_MODE_DONE_WAITING: - // Check if we have moved far enough that it looks more like a - // scroll than a tap - maybeStartScrolling(delta); - break; - - case TOUCH_MODE_DRAGGING: - case TOUCH_MODE_OVERSCROLL: - mLastTouchPos = pos; - maybeScroll(delta); - break; - } - - break; - } - - case MotionEvent.ACTION_CANCEL: - cancelCheckForTap(); - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - - setPressed(false); - View motionView = this.getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { - motionView.setPressed(false); - } - - if (mStartEdge != null && mEndEdge != null) { - needsInvalidate = mStartEdge.onRelease() | mEndEdge.onRelease(); - } - - recycleVelocityTracker(); - - break; - - case MotionEvent.ACTION_UP: { - switch (mTouchMode) { - case TOUCH_MODE_DOWN: - case TOUCH_MODE_TAP: - case TOUCH_MODE_DONE_WAITING: { - final int motionPosition = mMotionPosition; - final View child = getChildAt(motionPosition - mFirstPosition); - - final float x = ev.getX(); - final float y = ev.getY(); - - boolean inList = false; - if (mIsVertical) { - inList = x > getPaddingLeft() - && x < getWidth() - getPaddingRight(); - } else { - inList = y > getPaddingTop() - && y < getHeight() - getPaddingBottom(); - } - - if (child != null && !child.hasFocusable() && inList) { - if (mTouchMode != TOUCH_MODE_DOWN) { - child.setPressed(false); - } - - if (mPerformClick == null) { - mPerformClick = new PerformClick(); - } - - final PerformClick performClick = mPerformClick; - performClick.mClickMotionPosition = motionPosition; - performClick.rememberWindowAttachCount(); - - mResurrectToPosition = motionPosition; - - if (mTouchMode == TOUCH_MODE_DOWN - || mTouchMode == TOUCH_MODE_TAP) { - if (mTouchMode == TOUCH_MODE_DOWN) { - cancelCheckForTap(); - } else { - cancelCheckForLongPress(); - } - - mLayoutMode = LAYOUT_NORMAL; - - if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { - mTouchMode = TOUCH_MODE_TAP; - - setPressed(true); - positionSelector(mMotionPosition, child); - child.setPressed(true); - - if (mSelector != null) { - Drawable d = mSelector.getCurrent(); - if (d != null - && d instanceof TransitionDrawable) { - ((TransitionDrawable) d).resetTransition(); - } - } - - if (mTouchModeReset != null) { - removeCallbacks(mTouchModeReset); - } - - mTouchModeReset = new Runnable() { - @Override - public void run() { - mTouchMode = TOUCH_MODE_REST; - - setPressed(false); - child.setPressed(false); - - if (!mDataChanged) { - performClick.run(); - } - - mTouchModeReset = null; - } - }; - - postDelayed(mTouchModeReset, - ViewConfiguration.getPressedStateDuration()); - } else { - mTouchMode = TOUCH_MODE_REST; - updateSelectorState(); - } - } else if (!mDataChanged - && mAdapter.isEnabled(motionPosition)) { - performClick.run(); - } - } - - mTouchMode = TOUCH_MODE_REST; - updateSelectorState(); - - break; - } - - case TOUCH_MODE_DRAGGING: - if (contentFits()) { - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - break; - } - - mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - - final float velocity; - if (mIsVertical) { - velocity = VelocityTrackerCompat.getYVelocity( - mVelocityTracker, mActivePointerId); - } else { - velocity = VelocityTrackerCompat.getXVelocity( - mVelocityTracker, mActivePointerId); - } - - if (Math.abs(velocity) >= mFlingVelocity) { - mTouchMode = TOUCH_MODE_FLINGING; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); - - mScroller.fling(0, 0, (int) (mIsVertical ? 0 : velocity), - (int) (mIsVertical ? velocity : 0), - (mIsVertical ? 0 : Integer.MIN_VALUE), - (mIsVertical ? 0 : Integer.MAX_VALUE), - (mIsVertical ? Integer.MIN_VALUE : 0), - (mIsVertical ? Integer.MAX_VALUE : 0)); - - mLastTouchPos = 0; - needsInvalidate = true; - } else { - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - } - - break; - - case TOUCH_MODE_OVERSCROLL: - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - break; - } - - cancelCheckForTap(); - cancelCheckForLongPress(); - setPressed(false); - - if (mStartEdge != null && mEndEdge != null) { - needsInvalidate |= mStartEdge.onRelease() - | mEndEdge.onRelease(); - } - - recycleVelocityTracker(); - - break; - } - } - - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(this); - } - - return true; - } - - @Override - public void onTouchModeChanged(boolean isInTouchMode) { - if (isInTouchMode) { - // Get rid of the selection when we enter touch mode - hideSelector(); - - // Layout, but only if we already have done so previously. - // (Otherwise may clobber a LAYOUT_SYNC layout that was requested to - // restore - // state.) - if (getWidth() > 0 && getHeight() > 0 && getChildCount() > 0) { - layoutChildren(); - } - - updateSelectorState(); - } else { - final int touchMode = mTouchMode; - if (touchMode == TOUCH_MODE_OVERSCROLL) { - if (mOverScroll != 0) { - mOverScroll = 0; - finishEdgeGlows(); - invalidate(); - } - } - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return handleKeyEvent(keyCode, 1, event); - } - - @Override - public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { - return handleKeyEvent(keyCode, repeatCount, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return handleKeyEvent(keyCode, 1, event); - } - - @Override - public void sendAccessibilityEvent(int eventType) { - // Since this class calls onScrollChanged even if the mFirstPosition and - // the - // child count have not changed we will avoid sending duplicate - // accessibility - // events. - if (eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) { - final int firstVisiblePosition = getFirstVisiblePosition(); - final int lastVisiblePosition = getLastVisiblePosition(); - - if (mLastAccessibilityScrollEventFromIndex == firstVisiblePosition - && mLastAccessibilityScrollEventToIndex == lastVisiblePosition) { - return; - } else { - mLastAccessibilityScrollEventFromIndex = firstVisiblePosition; - mLastAccessibilityScrollEventToIndex = lastVisiblePosition; - } - } - - super.sendAccessibilityEvent(eventType); - } - - @Override - @TargetApi(14) - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - event.setClassName(TwoWayView.class.getName()); - } - - @Override - @TargetApi(14) - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(TwoWayView.class.getName()); - - AccessibilityNodeInfoCompat infoCompat = new AccessibilityNodeInfoCompat( - info); - - if (isEnabled()) { - if (getFirstVisiblePosition() > 0) { - infoCompat - .addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD); - } - - if (getLastVisiblePosition() < getCount() - 1) { - infoCompat - .addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD); - } - } - } - - @Override - @TargetApi(16) - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (super.performAccessibilityAction(action, arguments)) { - return true; - } - - switch (action) { - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: - if (isEnabled() && getLastVisiblePosition() < getCount() - 1) { - final int viewportSize; - if (mIsVertical) { - viewportSize = getHeight() - getPaddingTop() - - getPaddingBottom(); - } else { - viewportSize = getWidth() - getPaddingLeft() - - getPaddingRight(); - } - - // TODO: Use some form of smooth scroll instead - trackMotionScroll(viewportSize); - return true; - } - return false; - - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: - if (isEnabled() && mFirstPosition > 0) { - final int viewportSize; - if (mIsVertical) { - viewportSize = getHeight() - getPaddingTop() - - getPaddingBottom(); - } else { - viewportSize = getWidth() - getPaddingLeft() - - getPaddingRight(); - } - - // TODO: Use some form of smooth scroll instead - trackMotionScroll(-viewportSize); - return true; - } - return false; - } - - return false; - } - - /** - * Return true if child is an ancestor of parent, (or equal to the parent). - */ - private boolean isViewAncestorOf(View child, View parent) { - if (child == parent) { - return true; - } - - final ViewParent theParent = child.getParent(); - - return (theParent instanceof ViewGroup) - && isViewAncestorOf((View) theParent, parent); - } - - private void forceValidFocusDirection(int direction) { - if (mIsVertical && direction != View.FOCUS_UP - && direction != View.FOCUS_DOWN) { - throw new IllegalArgumentException( - "Focus direction must be one of" - + " {View.FOCUS_UP, View.FOCUS_DOWN} for vertical orientation"); - } else if (!mIsVertical && direction != View.FOCUS_LEFT - && direction != View.FOCUS_RIGHT) { - throw new IllegalArgumentException( - "Focus direction must be one of" - + " {View.FOCUS_LEFT, View.FOCUS_RIGHT} for vertical orientation"); - } - } - - private void forceValidInnerFocusDirection(int direction) { - if (mIsVertical && direction != View.FOCUS_LEFT - && direction != View.FOCUS_RIGHT) { - throw new IllegalArgumentException( - "Direction must be one of" - + " {View.FOCUS_LEFT, View.FOCUS_RIGHT} for vertical orientation"); - } else if (!mIsVertical && direction != View.FOCUS_UP - && direction != View.FOCUS_DOWN) { - throw new IllegalArgumentException( - "direction must be one of" - + " {View.FOCUS_UP, View.FOCUS_DOWN} for horizontal orientation"); - } - } - - /** - * Scrolls up or down by the number of items currently present on screen. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return whether selection was moved - */ - boolean pageScroll(int direction) { - forceValidFocusDirection(direction); - - boolean forward = false; - int nextPage = -1; - - if (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT) { - nextPage = Math.max(0, mSelectedPosition - getChildCount() - 1); - } else if (direction == View.FOCUS_DOWN - || direction == View.FOCUS_RIGHT) { - nextPage = Math.min(mItemCount - 1, mSelectedPosition - + getChildCount() - 1); - forward = true; - } - - if (nextPage < 0) { - return false; - } - - final int position = lookForSelectablePosition(nextPage, forward); - if (position >= 0) { - mLayoutMode = LAYOUT_SPECIFIC; - mSpecificStart = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - - if (forward && position > mItemCount - getChildCount()) { - mLayoutMode = LAYOUT_FORCE_BOTTOM; - } - - if (!forward && position < getChildCount()) { - mLayoutMode = LAYOUT_FORCE_TOP; - } - - setSelectionInt(position); - invokeOnItemScrollListener(); - - if (!awakenScrollbarsInternal()) { - invalidate(); - } - - return true; - } - - return false; - } - - /** - * Go to the last or first item if possible (not worrying about panning - * across or navigating within the internal focus of the currently selected - * item.) - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return whether selection was moved - */ - boolean fullScroll(int direction) { - forceValidFocusDirection(direction); - - boolean moved = false; - if (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT) { - if (mSelectedPosition != 0) { - int position = lookForSelectablePosition(0, true); - if (position >= 0) { - mLayoutMode = LAYOUT_FORCE_TOP; - setSelectionInt(position); - invokeOnItemScrollListener(); - } - - moved = true; - } - } else if (direction == View.FOCUS_DOWN - || direction == View.FOCUS_RIGHT) { - if (mSelectedPosition < mItemCount - 1) { - int position = lookForSelectablePosition(mItemCount - 1, true); - if (position >= 0) { - mLayoutMode = LAYOUT_FORCE_BOTTOM; - setSelectionInt(position); - invokeOnItemScrollListener(); - } - - moved = true; - } - } - - if (moved && !awakenScrollbarsInternal()) { - awakenScrollbarsInternal(); - invalidate(); - } - - return moved; - } - - /** - * To avoid horizontal/vertical focus searches changing the selected item, - * we manually focus search within the selected item (as applicable), and - * prevent focus from jumping to something within another item. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return Whether this consumes the key event. - */ - private boolean handleFocusWithinItem(int direction) { - forceValidInnerFocusDirection(direction); - - final int numChildren = getChildCount(); - - if (mItemsCanFocus && numChildren > 0 - && mSelectedPosition != INVALID_POSITION) { - final View selectedView = getSelectedView(); - - if (selectedView != null && selectedView.hasFocus() - && selectedView instanceof ViewGroup) { - - final View currentFocus = selectedView.findFocus(); - final View nextFocus = FocusFinder.getInstance().findNextFocus( - (ViewGroup) selectedView, currentFocus, direction); - - if (nextFocus != null) { - // Do the math to get interesting rect in next focus' - // coordinates - currentFocus.getFocusedRect(mTempRect); - offsetDescendantRectToMyCoords(currentFocus, mTempRect); - offsetRectIntoDescendantCoords(nextFocus, mTempRect); - - if (nextFocus.requestFocus(direction, mTempRect)) { - return true; - } - } - - // We are blocking the key from being handled (by returning - // true) - // if the global result is going to be some other view within - // this - // list. This is to achieve the overall goal of having - // horizontal/vertical - // d-pad navigation remain in the current item depending on the - // current - // orientation in this view. - final View globalNextFocus = FocusFinder.getInstance() - .findNextFocus((ViewGroup) getRootView(), currentFocus, - direction); - - if (globalNextFocus != null) { - return isViewAncestorOf(globalNextFocus, this); - } - } - } - - return false; - } - - /** - * Scrolls to the next or previous item if possible. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return whether selection was moved - */ - private boolean arrowScroll(int direction) { - forceValidFocusDirection(direction); - - try { - mInLayout = true; - - final boolean handled = arrowScrollImpl(direction); - if (handled) { - playSoundEffect(SoundEffectConstants - .getContantForFocusDirection(direction)); - } - - return handled; - } finally { - mInLayout = false; - } - } - - /** - * When selection changes, it is possible that the previously selected or - * the next selected item will change its size. If so, we need to offset - * some folks, and re-layout the items as appropriate. - * - * @param selectedView - * The currently selected view (before changing selection). - * should be null if there was no previous - * selection. - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * @param newSelectedPosition - * The position of the next selection. - * @param newFocusAssigned - * whether new focus was assigned. This matters because when - * something has focus, we don't want to show selection (ugh). - */ - private void handleNewSelectionChange(View selectedView, int direction, - int newSelectedPosition, boolean newFocusAssigned) { - forceValidFocusDirection(direction); - - if (newSelectedPosition == INVALID_POSITION) { - throw new IllegalArgumentException( - "newSelectedPosition needs to be valid"); - } - - // Whether or not we are moving down/right or up/left, we want to - // preserve the - // top/left of whatever view is at the start: - // - moving down/right: the view that had selection - // - moving up/left: the view that is getting selection - final int selectedIndex = mSelectedPosition - mFirstPosition; - final int nextSelectedIndex = newSelectedPosition - mFirstPosition; - int startViewIndex, endViewIndex; - boolean topSelected = false; - View startView; - View endView; - - if (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT) { - startViewIndex = nextSelectedIndex; - endViewIndex = selectedIndex; - startView = getChildAt(startViewIndex); - endView = selectedView; - topSelected = true; - } else { - startViewIndex = selectedIndex; - endViewIndex = nextSelectedIndex; - startView = selectedView; - endView = getChildAt(endViewIndex); - } - - final int numChildren = getChildCount(); - - // start with top view: is it changing size? - if (startView != null) { - startView.setSelected(!newFocusAssigned && topSelected); - measureAndAdjustDown(startView, startViewIndex, numChildren); - } - - // is the bottom view changing size? - if (endView != null) { - endView.setSelected(!newFocusAssigned && !topSelected); - measureAndAdjustDown(endView, endViewIndex, numChildren); - } - } - - /** - * Re-measure a child, and if its height changes, lay it out preserving its - * top, and adjust the children below it appropriately. - * - * @param child - * The child - * @param childIndex - * The view group index of the child. - * @param numChildren - * The number of children in the view group. - */ - private void measureAndAdjustDown(View child, int childIndex, - int numChildren) { - int oldHeight = child.getHeight(); - measureChild(child); - - if (child.getMeasuredHeight() == oldHeight) { - return; - } - - // lay out the view, preserving its top - relayoutMeasuredChild(child); - - // adjust views below appropriately - final int heightDelta = child.getMeasuredHeight() - oldHeight; - for (int i = childIndex + 1; i < numChildren; i++) { - getChildAt(i).offsetTopAndBottom(heightDelta); - } - } - - /** - * Do an arrow scroll based on focus searching. If a new view is given - * focus, return the selection delta and amount to scroll via an - * {@link ArrowScrollFocusResult}, otherwise, return null. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return The result if focus has changed, or null. - */ - private ArrowScrollFocusResult arrowScrollFocused(final int direction) { - forceValidFocusDirection(direction); - - final View selectedView = getSelectedView(); - final View newFocus; - final int searchPoint; - - if (selectedView != null && selectedView.hasFocus()) { - View oldFocus = selectedView.findFocus(); - newFocus = FocusFinder.getInstance().findNextFocus(this, oldFocus, - direction); - } else { - if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) { - final int start = (mIsVertical ? getPaddingTop() - : getPaddingLeft()); - - final int selectedStart; - if (selectedView != null) { - selectedStart = (mIsVertical ? selectedView.getTop() - : selectedView.getLeft()); - } else { - selectedStart = start; - } - - searchPoint = Math.max(selectedStart, start); - } else { - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - final int selectedEnd; - if (selectedView != null) { - selectedEnd = (mIsVertical ? selectedView.getBottom() - : selectedView.getRight()); - } else { - selectedEnd = end; - } - - searchPoint = Math.min(selectedEnd, end); - } - - final int x = (mIsVertical ? 0 : searchPoint); - final int y = (mIsVertical ? searchPoint : 0); - mTempRect.set(x, y, x, y); - - newFocus = FocusFinder.getInstance().findNextFocusFromRect(this, - mTempRect, direction); - } - - if (newFocus != null) { - final int positionOfNewFocus = positionOfNewFocus(newFocus); - - // If the focus change is in a different new position, make sure - // we aren't jumping over another selectable position. - if (mSelectedPosition != INVALID_POSITION - && positionOfNewFocus != mSelectedPosition) { - final int selectablePosition = lookForSelectablePositionOnScreen(direction); - - final boolean movingForward = (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT); - final boolean movingBackward = (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT); - - if (selectablePosition != INVALID_POSITION - && ((movingForward && selectablePosition < positionOfNewFocus) || (movingBackward && selectablePosition > positionOfNewFocus))) { - return null; - } - } - - int focusScroll = amountToScrollToNewFocus(direction, newFocus, - positionOfNewFocus); - - final int maxScrollAmount = getMaxScrollAmount(); - if (focusScroll < maxScrollAmount) { - // Not moving too far, safe to give next view focus - newFocus.requestFocus(direction); - mArrowScrollFocusResult.populate(positionOfNewFocus, - focusScroll); - return mArrowScrollFocusResult; - } else if (distanceToView(newFocus) < maxScrollAmount) { - // Case to consider: - // Too far to get entire next focusable on screen, but by going - // max scroll amount, we are getting it at least partially in - // view, - // so give it focus and scroll the max amount. - newFocus.requestFocus(direction); - mArrowScrollFocusResult.populate(positionOfNewFocus, - maxScrollAmount); - return mArrowScrollFocusResult; - } - } - - return null; - } - - /** - * @return The maximum amount a list view will scroll in response to an - * arrow event. - */ - public int getMaxScrollAmount() { - return (int) (MAX_SCROLL_FACTOR * getHeight()); - } - - /** - * @return The amount to preview next items when arrow scrolling. - */ - private int getArrowScrollPreviewLength() { - // FIXME: TwoWayView has no fading edge support just yet but using it - // makes it convenient for defining the next item's previous length. - int fadingEdgeLength = (mIsVertical ? getVerticalFadingEdgeLength() - : getHorizontalFadingEdgeLength()); - - return mItemMargin - + Math.max(MIN_SCROLL_PREVIEW_PIXELS, fadingEdgeLength); - } - - /** - * @param newFocus - * The view that would have focus. - * @return the position that contains newFocus - */ - private int positionOfNewFocus(View newFocus) { - final int numChildren = getChildCount(); - - for (int i = 0; i < numChildren; i++) { - final View child = getChildAt(i); - if (isViewAncestorOf(newFocus, child)) { - return mFirstPosition + i; - } - } - - throw new IllegalArgumentException( - "newFocus is not a child of any of the" - + " children of the list!"); - } - - /** - * Handle an arrow scroll going up or down. Take into account whether items - * are selectable, whether there are focusable items, etc. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return Whether any scrolling, selection or focus change occurred. - */ - private boolean arrowScrollImpl(int direction) { - forceValidFocusDirection(direction); - - if (getChildCount() <= 0) { - return false; - } - - View selectedView = getSelectedView(); - int selectedPos = mSelectedPosition; - - int nextSelectedPosition = lookForSelectablePositionOnScreen(direction); - int amountToScroll = amountToScroll(direction, nextSelectedPosition); - - // If we are moving focus, we may OVERRIDE the default behaviour - final ArrowScrollFocusResult focusResult = (mItemsCanFocus ? arrowScrollFocused(direction) - : null); - if (focusResult != null) { - nextSelectedPosition = focusResult.getSelectedPosition(); - amountToScroll = focusResult.getAmountToScroll(); - } - - boolean needToRedraw = (focusResult != null); - if (nextSelectedPosition != INVALID_POSITION) { - handleNewSelectionChange(selectedView, direction, - nextSelectedPosition, focusResult != null); - - setSelectedPositionInt(nextSelectedPosition); - setNextSelectedPositionInt(nextSelectedPosition); - - selectedView = getSelectedView(); - selectedPos = nextSelectedPosition; - - if (mItemsCanFocus && focusResult == null) { - // There was no new view found to take focus, make sure we - // don't leave focus with the old selection. - final View focused = getFocusedChild(); - if (focused != null) { - focused.clearFocus(); - } - } - - needToRedraw = true; - checkSelectionChanged(); - } - - if (amountToScroll > 0) { - trackMotionScroll(direction == View.FOCUS_UP - || direction == View.FOCUS_LEFT ? amountToScroll - : -amountToScroll); - needToRedraw = true; - } - - // If we didn't find a new focusable, make sure any existing focused - // item that was panned off screen gives up focus. - if (mItemsCanFocus && focusResult == null && selectedView != null - && selectedView.hasFocus()) { - final View focused = selectedView.findFocus(); - if (!isViewAncestorOf(focused, this) || distanceToView(focused) > 0) { - focused.clearFocus(); - } - } - - // If the current selection is panned off, we need to remove the - // selection - if (nextSelectedPosition == INVALID_POSITION && selectedView != null - && !isViewAncestorOf(selectedView, this)) { - selectedView = null; - hideSelector(); - - // But we don't want to set the ressurect position (that would make - // subsequent - // unhandled key events bring back the item we just scrolled off) - mResurrectToPosition = INVALID_POSITION; - } - - if (needToRedraw) { - if (selectedView != null) { - positionSelector(selectedPos, selectedView); - mSelectedStart = selectedView.getTop(); - } - - if (!awakenScrollbarsInternal()) { - invalidate(); - } - - invokeOnItemScrollListener(); - return true; - } - - return false; - } - - /** - * Determine how much we need to scroll in order to get the next selected - * view visible. The amount is capped at {@link #getMaxScrollAmount()}. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * @param nextSelectedPosition - * The position of the next selection, or - * {@link #INVALID_POSITION} if there is no next selectable - * position - * - * @return The amount to scroll. Note: this is always positive! Direction - * needs to be taken into account when actually scrolling. - */ - private int amountToScroll(int direction, int nextSelectedPosition) { - forceValidFocusDirection(direction); - - final int numChildren = getChildCount(); - - if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) { - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - int indexToMakeVisible = numChildren - 1; - if (nextSelectedPosition != INVALID_POSITION) { - indexToMakeVisible = nextSelectedPosition - mFirstPosition; - } - - final int positionToMakeVisible = mFirstPosition - + indexToMakeVisible; - final View viewToMakeVisible = getChildAt(indexToMakeVisible); - - int goalEnd = end; - if (positionToMakeVisible < mItemCount - 1) { - goalEnd -= getArrowScrollPreviewLength(); - } - - final int viewToMakeVisibleStart = (mIsVertical ? viewToMakeVisible - .getTop() : viewToMakeVisible.getLeft()); - final int viewToMakeVisibleEnd = (mIsVertical ? viewToMakeVisible - .getBottom() : viewToMakeVisible.getRight()); - - if (viewToMakeVisibleEnd <= goalEnd) { - // Target item is fully visible - return 0; - } - - if (nextSelectedPosition != INVALID_POSITION - && (goalEnd - viewToMakeVisibleStart) >= getMaxScrollAmount()) { - // Item already has enough of it visible, changing selection is - // good enough - return 0; - } - - int amountToScroll = (viewToMakeVisibleEnd - goalEnd); - - if (mFirstPosition + numChildren == mItemCount) { - final View lastChild = getChildAt(numChildren - 1); - final int lastChildEnd = (mIsVertical ? lastChild.getBottom() - : lastChild.getRight()); - - // Last is last in list -> Make sure we don't scroll past it - final int max = lastChildEnd - end; - amountToScroll = Math.min(amountToScroll, max); - } - - return Math.min(amountToScroll, getMaxScrollAmount()); - } else { - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - - int indexToMakeVisible = 0; - if (nextSelectedPosition != INVALID_POSITION) { - indexToMakeVisible = nextSelectedPosition - mFirstPosition; - } - - final int positionToMakeVisible = mFirstPosition - + indexToMakeVisible; - final View viewToMakeVisible = getChildAt(indexToMakeVisible); - - int goalStart = start; - if (positionToMakeVisible > 0) { - goalStart += getArrowScrollPreviewLength(); - } - - final int viewToMakeVisibleStart = (mIsVertical ? viewToMakeVisible - .getTop() : viewToMakeVisible.getLeft()); - final int viewToMakeVisibleEnd = (mIsVertical ? viewToMakeVisible - .getBottom() : viewToMakeVisible.getRight()); - - if (viewToMakeVisibleStart >= goalStart) { - // Item is fully visible - return 0; - } - - if (nextSelectedPosition != INVALID_POSITION - && (viewToMakeVisibleEnd - goalStart) >= getMaxScrollAmount()) { - // Item already has enough of it visible, changing selection is - // good enough - return 0; - } - - int amountToScroll = (goalStart - viewToMakeVisibleStart); - - if (mFirstPosition == 0) { - final View firstChild = getChildAt(0); - final int firstChildStart = (mIsVertical ? firstChild.getTop() - : firstChild.getLeft()); - - // First is first in list -> make sure we don't scroll past it - final int max = start - firstChildStart; - amountToScroll = Math.min(amountToScroll, max); - } - - return Math.min(amountToScroll, getMaxScrollAmount()); - } - } - - /** - * Determine how much we need to scroll in order to get newFocus in view. - * - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * @param newFocus - * The view that would take focus. - * @param positionOfNewFocus - * The position of the list item containing newFocus - * - * @return The amount to scroll. Note: this is always positive! Direction - * needs to be taken into account when actually scrolling. - */ - private int amountToScrollToNewFocus(int direction, View newFocus, - int positionOfNewFocus) { - forceValidFocusDirection(direction); - - int amountToScroll = 0; - - newFocus.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(newFocus, mTempRect); - - if (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT) { - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - final int newFocusStart = (mIsVertical ? mTempRect.top - : mTempRect.left); - - if (newFocusStart < start) { - amountToScroll = start - newFocusStart; - if (positionOfNewFocus > 0) { - amountToScroll += getArrowScrollPreviewLength(); - } - } - } else { - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - final int newFocusEnd = (mIsVertical ? mTempRect.bottom - : mTempRect.right); - - if (newFocusEnd > end) { - amountToScroll = newFocusEnd - end; - if (positionOfNewFocus < mItemCount - 1) { - amountToScroll += getArrowScrollPreviewLength(); - } - } - } - - return amountToScroll; - } - - /** - * Determine the distance to the nearest edge of a view in a particular - * direction. - * - * @param descendant - * A descendant of this list. - * @return The distance, or 0 if the nearest edge is already on screen. - */ - private int distanceToView(View descendant) { - descendant.getDrawingRect(mTempRect); - offsetDescendantRectToMyCoords(descendant, mTempRect); - - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - final int viewStart = (mIsVertical ? mTempRect.top : mTempRect.left); - final int viewEnd = (mIsVertical ? mTempRect.bottom : mTempRect.right); - - int distance = 0; - if (viewEnd < start) { - distance = start - viewEnd; - } else if (viewStart > end) { - distance = viewStart - end; - } - - return distance; - } - - private boolean handleKeyScroll(KeyEvent event, int count, int direction) { - boolean handled = false; - - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded(); - if (!handled) { - while (count-- > 0) { - if (arrowScroll(direction)) { - handled = true; - } else { - break; - } - } - } - } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_ALT_ON)) { - handled = resurrectSelectionIfNeeded() || fullScroll(direction); - } - - return handled; - } - - private boolean handleKeyEvent(int keyCode, int count, KeyEvent event) { - if (mAdapter == null || !mIsAttached) { - return false; - } - - if (mDataChanged) { - layoutChildren(); - } - - boolean handled = false; - final int action = event.getAction(); - - if (action != KeyEvent.ACTION_UP) { - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_UP: - if (mIsVertical) { - handled = handleKeyScroll(event, count, View.FOCUS_UP); - } else if (KeyEventCompat.hasNoModifiers(event)) { - handled = handleFocusWithinItem(View.FOCUS_UP); - } - break; - - case KeyEvent.KEYCODE_DPAD_DOWN: { - if (mIsVertical) { - handled = handleKeyScroll(event, count, View.FOCUS_DOWN); - } else if (KeyEventCompat.hasNoModifiers(event)) { - handled = handleFocusWithinItem(View.FOCUS_DOWN); - } - break; - } - - case KeyEvent.KEYCODE_DPAD_LEFT: - if (!mIsVertical) { - handled = handleKeyScroll(event, count, View.FOCUS_LEFT); - } else if (KeyEventCompat.hasNoModifiers(event)) { - handled = handleFocusWithinItem(View.FOCUS_LEFT); - } - break; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!mIsVertical) { - handled = handleKeyScroll(event, count, View.FOCUS_RIGHT); - } else if (KeyEventCompat.hasNoModifiers(event)) { - handled = handleFocusWithinItem(View.FOCUS_RIGHT); - } - break; - - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded(); - if (!handled && event.getRepeatCount() == 0 - && getChildCount() > 0) { - keyPressed(); - handled = true; - } - } - break; - - case KeyEvent.KEYCODE_SPACE: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded() - || pageScroll(mIsVertical ? View.FOCUS_DOWN - : View.FOCUS_RIGHT); - } else if (KeyEventCompat.hasModifiers(event, - KeyEvent.META_SHIFT_ON)) { - handled = resurrectSelectionIfNeeded() - || fullScroll(mIsVertical ? View.FOCUS_UP - : View.FOCUS_LEFT); - } - - handled = true; - break; - - case KeyEvent.KEYCODE_PAGE_UP: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded() - || pageScroll(mIsVertical ? View.FOCUS_UP - : View.FOCUS_LEFT); - } else if (KeyEventCompat.hasModifiers(event, - KeyEvent.META_ALT_ON)) { - handled = resurrectSelectionIfNeeded() - || fullScroll(mIsVertical ? View.FOCUS_UP - : View.FOCUS_LEFT); - } - break; - - case KeyEvent.KEYCODE_PAGE_DOWN: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded() - || pageScroll(mIsVertical ? View.FOCUS_DOWN - : View.FOCUS_RIGHT); - } else if (KeyEventCompat.hasModifiers(event, - KeyEvent.META_ALT_ON)) { - handled = resurrectSelectionIfNeeded() - || fullScroll(mIsVertical ? View.FOCUS_DOWN - : View.FOCUS_RIGHT); - } - break; - - case KeyEvent.KEYCODE_MOVE_HOME: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded() - || fullScroll(mIsVertical ? View.FOCUS_UP - : View.FOCUS_LEFT); - } - break; - - case KeyEvent.KEYCODE_MOVE_END: - if (KeyEventCompat.hasNoModifiers(event)) { - handled = resurrectSelectionIfNeeded() - || fullScroll(mIsVertical ? View.FOCUS_DOWN - : View.FOCUS_RIGHT); - } - break; - } - } - - if (handled) { - return true; - } - - switch (action) { - case KeyEvent.ACTION_DOWN: - return super.onKeyDown(keyCode, event); - - case KeyEvent.ACTION_UP: - if (!isEnabled()) { - return true; - } - - if (isClickable() && isPressed() && mSelectedPosition >= 0 - && mAdapter != null - && mSelectedPosition < mAdapter.getCount()) { - - final View child = getChildAt(mSelectedPosition - - mFirstPosition); - if (child != null) { - performItemClick(child, mSelectedPosition, mSelectedRowId); - child.setPressed(false); - } - - setPressed(false); - return true; - } - - return false; - - case KeyEvent.ACTION_MULTIPLE: - return super.onKeyMultiple(keyCode, count, event); - - default: - return false; - } - } - - private void initOrResetVelocityTracker() { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } else { - mVelocityTracker.clear(); - } - } - - private void initVelocityTrackerIfNotExists() { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - } - - private void recycleVelocityTracker() { - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - /** - * Notify our scroll listener (if there is one) of a change in scroll state - */ - private void invokeOnItemScrollListener() { - if (mOnScrollListener != null) { - mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), - mItemCount); - } - - // Dummy values, View's implementation does not use these. - onScrollChanged(0, 0, 0, 0); - } - - private void reportScrollStateChange(int newState) { - if (newState == mLastScrollState) { - return; - } - - if (mOnScrollListener != null) { - mLastScrollState = newState; - mOnScrollListener.onScrollStateChanged(this, newState); - } - } - - private boolean maybeStartScrolling(int delta) { - final boolean isOverScroll = (mOverScroll != 0); - if (Math.abs(delta) <= mTouchSlop && !isOverScroll) { - return false; - } - - if (isOverScroll) { - mTouchMode = TOUCH_MODE_OVERSCROLL; - } else { - mTouchMode = TOUCH_MODE_DRAGGING; - } - - // Time to start stealing events! Once we've stolen them, don't - // let anyone steal from us. - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - - cancelCheckForLongPress(); - - setPressed(false); - View motionView = getChildAt(mMotionPosition - mFirstPosition); - if (motionView != null) { - motionView.setPressed(false); - } - - reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); - - return true; - } - - private void maybeScroll(int delta) { - if (mTouchMode == TOUCH_MODE_DRAGGING) { - handleDragChange(delta); - } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) { - handleOverScrollChange(delta); - } - } - - private void handleDragChange(int delta) { - // Time to start stealing events! Once we've stolen them, don't - // let anyone steal from us. - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - - final int motionIndex; - if (mMotionPosition >= 0) { - motionIndex = mMotionPosition - mFirstPosition; - } else { - // If we don't have a motion position that we can reliably track, - // pick something in the middle to make a best guess at things - // below. - motionIndex = getChildCount() / 2; - } - - int motionViewPrevStart = 0; - View motionView = this.getChildAt(motionIndex); - if (motionView != null) { - motionViewPrevStart = (mIsVertical ? motionView.getTop() - : motionView.getLeft()); - } - - boolean atEdge = trackMotionScroll(delta); - - motionView = this.getChildAt(motionIndex); - if (motionView != null) { - final int motionViewRealStart = (mIsVertical ? motionView.getTop() - : motionView.getLeft()); - - if (atEdge) { - final int overscroll = -delta - - (motionViewRealStart - motionViewPrevStart); - updateOverScrollState(delta, overscroll); - } - } - } - - private void updateOverScrollState(int delta, int overscroll) { - overScrollByInternal((mIsVertical ? 0 : overscroll), - (mIsVertical ? overscroll : 0), - (mIsVertical ? 0 : mOverScroll), - (mIsVertical ? mOverScroll : 0), 0, 0, (mIsVertical ? 0 - : mOverscrollDistance), - (mIsVertical ? mOverscrollDistance : 0), true); - - if (Math.abs(mOverscrollDistance) == Math.abs(mOverScroll)) { - // Break fling velocity if we impacted an edge - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - - final int overscrollMode = ViewCompat.getOverScrollMode(this); - if (overscrollMode == ViewCompat.OVER_SCROLL_ALWAYS - || (overscrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { - mTouchMode = TOUCH_MODE_OVERSCROLL; - - float pull = (float) overscroll - / (mIsVertical ? getHeight() : getWidth()); - if (delta > 0) { - mStartEdge.onPull(pull); - - if (!mEndEdge.isFinished()) { - mEndEdge.onRelease(); - } - } else if (delta < 0) { - mEndEdge.onPull(pull); - - if (!mStartEdge.isFinished()) { - mStartEdge.onRelease(); - } - } - - if (delta != 0) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - } - - private void handleOverScrollChange(int delta) { - final int oldOverScroll = mOverScroll; - final int newOverScroll = oldOverScroll - delta; - - int overScrollDistance = -delta; - if ((newOverScroll < 0 && oldOverScroll >= 0) - || (newOverScroll > 0 && oldOverScroll <= 0)) { - overScrollDistance = -oldOverScroll; - delta += overScrollDistance; - } else { - delta = 0; - } - - if (overScrollDistance != 0) { - updateOverScrollState(delta, overScrollDistance); - } - - if (delta != 0) { - if (mOverScroll != 0) { - mOverScroll = 0; - ViewCompat.postInvalidateOnAnimation(this); - } - - trackMotionScroll(delta); - mTouchMode = TOUCH_MODE_DRAGGING; - - // We did not scroll the full amount. Treat this essentially like - // the - // start of a new touch scroll - mMotionPosition = findClosestMotionRowOrColumn((int) mLastTouchPos); - mTouchRemainderPos = 0; - } - } - - /** - * What is the distance between the source and destination rectangles given - * the direction of focus navigation between them? The direction basically - * helps figure out more quickly what is self evident by the relationship - * between the rects... - * - * @param source - * the source rectangle - * @param dest - * the destination rectangle - * @param direction - * the direction - * @return the distance between the rectangles - */ - private static int getDistance(Rect source, Rect dest, int direction) { - int sX, sY; // source x, y - int dX, dY; // dest x, y - - switch (direction) { - case View.FOCUS_RIGHT: - sX = source.right; - sY = source.top + source.height() / 2; - dX = dest.left; - dY = dest.top + dest.height() / 2; - break; - - case View.FOCUS_DOWN: - sX = source.left + source.width() / 2; - sY = source.bottom; - dX = dest.left + dest.width() / 2; - dY = dest.top; - break; - - case View.FOCUS_LEFT: - sX = source.left; - sY = source.top + source.height() / 2; - dX = dest.right; - dY = dest.top + dest.height() / 2; - break; - - case View.FOCUS_UP: - sX = source.left + source.width() / 2; - sY = source.top; - dX = dest.left + dest.width() / 2; - dY = dest.bottom; - break; - - case View.FOCUS_FORWARD: - case View.FOCUS_BACKWARD: - sX = source.right + source.width() / 2; - sY = source.top + source.height() / 2; - dX = dest.left + dest.width() / 2; - dY = dest.top + dest.height() / 2; - break; - - default: - throw new IllegalArgumentException("direction must be one of " - + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, " - + "FOCUS_FORWARD, FOCUS_BACKWARD}."); - } - - int deltaX = dX - sX; - int deltaY = dY - sY; - - return deltaY * deltaY + deltaX * deltaX; - } - - private int findMotionRowOrColumn(int motionPos) { - int childCount = getChildCount(); - if (childCount == 0) { - return INVALID_POSITION; - } - - for (int i = 0; i < childCount; i++) { - View v = getChildAt(i); - - if ((mIsVertical && motionPos <= v.getBottom()) - || (!mIsVertical && motionPos <= v.getRight())) { - return mFirstPosition + i; - } - } - - return INVALID_POSITION; - } - - private int findClosestMotionRowOrColumn(int motionPos) { - final int childCount = getChildCount(); - if (childCount == 0) { - return INVALID_POSITION; - } - - final int motionRow = findMotionRowOrColumn(motionPos); - if (motionRow != INVALID_POSITION) { - return motionRow; - } else { - return mFirstPosition + childCount - 1; - } - } - - @TargetApi(9) - private int getScaledOverscrollDistance(ViewConfiguration vc) { - if (Build.VERSION.SDK_INT < 9) { - return 0; - } - - return vc.getScaledOverscrollDistance(); - } - - private boolean contentFits() { - final int childCount = getChildCount(); - if (childCount == 0) { - return true; - } - - if (childCount != mItemCount) { - return false; - } - - View first = getChildAt(0); - View last = getChildAt(childCount - 1); - - if (mIsVertical) { - return first.getTop() >= getPaddingTop() - && last.getBottom() <= getHeight() - getPaddingBottom(); - } else { - return first.getLeft() >= getPaddingLeft() - && last.getRight() <= getWidth() - getPaddingRight(); - } - } - - private void updateScrollbarsDirection() { - setHorizontalScrollBarEnabled(!mIsVertical); - setVerticalScrollBarEnabled(mIsVertical); - } - - private void triggerCheckForTap() { - if (mPendingCheckForTap == null) { - mPendingCheckForTap = new CheckForTap(); - } - - postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); - } - - private void cancelCheckForTap() { - if (mPendingCheckForTap == null) { - return; - } - - removeCallbacks(mPendingCheckForTap); - } - - private void triggerCheckForLongPress() { - if (mPendingCheckForLongPress == null) { - mPendingCheckForLongPress = new CheckForLongPress(); - } - - mPendingCheckForLongPress.rememberWindowAttachCount(); - - postDelayed(mPendingCheckForLongPress, - ViewConfiguration.getLongPressTimeout()); - } - - private void cancelCheckForLongPress() { - if (mPendingCheckForLongPress == null) { - return; - } - - removeCallbacks(mPendingCheckForLongPress); - } - - boolean trackMotionScroll(int incrementalDelta) { - final int childCount = getChildCount(); - if (childCount == 0) { - return true; - } - - final View first = getChildAt(0); - final int firstStart = (mIsVertical ? first.getTop() : first.getLeft()); - - final View last = getChildAt(childCount - 1); - final int lastEnd = (mIsVertical ? last.getBottom() : last.getRight()); - - final int paddingTop = getPaddingTop(); - final int paddingBottom = getPaddingBottom(); - final int paddingLeft = getPaddingLeft(); - final int paddingRight = getPaddingRight(); - - final int paddingStart = (mIsVertical ? paddingTop : paddingLeft); - - final int spaceBefore = paddingStart - firstStart; - final int end = (mIsVertical ? getHeight() - paddingBottom : getWidth() - - paddingRight); - final int spaceAfter = lastEnd - end; - - final int size; - if (mIsVertical) { - size = getHeight() - paddingBottom - paddingTop; - } else { - size = getWidth() - paddingRight - paddingLeft; - } - - if (incrementalDelta < 0) { - incrementalDelta = Math.max(-(size - 1), incrementalDelta); - } else { - incrementalDelta = Math.min(size - 1, incrementalDelta); - } - - final int firstPosition = mFirstPosition; - - final boolean cannotScrollDown = (firstPosition == 0 - && firstStart >= paddingStart && incrementalDelta >= 0); - final boolean cannotScrollUp = (firstPosition + childCount == mItemCount - && lastEnd <= end && incrementalDelta <= 0); - - if (cannotScrollDown || cannotScrollUp) { - return incrementalDelta != 0; - } - - final boolean inTouchMode = isInTouchMode(); - if (inTouchMode) { - hideSelector(); - } - - int start = 0; - int count = 0; - - final boolean down = (incrementalDelta < 0); - if (down) { - int childrenStart = -incrementalDelta + paddingStart; - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final int childEnd = (mIsVertical ? child.getBottom() : child - .getRight()); - - if (childEnd >= childrenStart) { - break; - } - - count++; - mRecycler.addScrapView(child, firstPosition + i); - } - } else { - int childrenEnd = end - incrementalDelta; - - for (int i = childCount - 1; i >= 0; i--) { - final View child = getChildAt(i); - final int childStart = (mIsVertical ? child.getTop() : child - .getLeft()); - - if (childStart <= childrenEnd) { - break; - } - - start = i; - count++; - mRecycler.addScrapView(child, firstPosition + i); - } - } - - mBlockLayoutRequests = true; - - if (count > 0) { - detachViewsFromParent(start, count); - } - - // invalidate before moving the children to avoid unnecessary invalidate - // calls to bubble up from the children all the way to the top - if (!awakenScrollbarsInternal()) { - invalidate(); - } - - offsetChildren(incrementalDelta); - - if (down) { - mFirstPosition += count; - } - - final int absIncrementalDelta = Math.abs(incrementalDelta); - if (spaceBefore < absIncrementalDelta - || spaceAfter < absIncrementalDelta) { - fillGap(down); - } - - if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { - final int childIndex = mSelectedPosition - mFirstPosition; - if (childIndex >= 0 && childIndex < getChildCount()) { - positionSelector(mSelectedPosition, getChildAt(childIndex)); - } - } else if (mSelectorPosition != INVALID_POSITION) { - final int childIndex = mSelectorPosition - mFirstPosition; - if (childIndex >= 0 && childIndex < getChildCount()) { - positionSelector(INVALID_POSITION, getChildAt(childIndex)); - } - } else { - mSelectorRect.setEmpty(); - } - - mBlockLayoutRequests = false; - - invokeOnItemScrollListener(); - - return false; - } - - @TargetApi(14) - private final float getCurrVelocity() { - if (Build.VERSION.SDK_INT >= 14) { - return mScroller.getCurrVelocity(); - } - - return 0; - } - - @TargetApi(5) - private boolean awakenScrollbarsInternal() { - if (Build.VERSION.SDK_INT >= 5) { - return super.awakenScrollBars(); - } else { - return false; - } - } - - @Override - public void computeScroll() { - if (!mScroller.computeScrollOffset()) { - return; - } - - final int pos; - if (mIsVertical) { - pos = mScroller.getCurrY(); - } else { - pos = mScroller.getCurrX(); - } - - final int diff = (int) (pos - mLastTouchPos); - mLastTouchPos = pos; - - final boolean stopped = trackMotionScroll(diff); - - if (!stopped && !mScroller.isFinished()) { - ViewCompat.postInvalidateOnAnimation(this); - } else { - if (stopped) { - final int overScrollMode = ViewCompat.getOverScrollMode(this); - if (overScrollMode != ViewCompat.OVER_SCROLL_NEVER) { - final EdgeEffectCompat edge = (diff > 0 ? mStartEdge - : mEndEdge); - - boolean needsInvalidate = edge.onAbsorb(Math - .abs((int) getCurrVelocity())); - - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - - mScroller.abortAnimation(); - } - - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - } - } - - private void finishEdgeGlows() { - if (mStartEdge != null) { - mStartEdge.finish(); - } - - if (mEndEdge != null) { - mEndEdge.finish(); - } - } - - private boolean drawStartEdge(Canvas canvas) { - if (mStartEdge.isFinished()) { - return false; - } - - if (mIsVertical) { - return mStartEdge.draw(canvas); - } - - final int restoreCount = canvas.save(); - final int height = getHeight() - getPaddingTop() - getPaddingBottom(); - - canvas.translate(0, height); - canvas.rotate(270); - - final boolean needsInvalidate = mStartEdge.draw(canvas); - canvas.restoreToCount(restoreCount); - return needsInvalidate; - } - - private boolean drawEndEdge(Canvas canvas) { - if (mEndEdge.isFinished()) { - return false; - } - - final int restoreCount = canvas.save(); - final int width = getWidth() - getPaddingLeft() - getPaddingRight(); - final int height = getHeight() - getPaddingTop() - getPaddingBottom(); - - if (mIsVertical) { - canvas.translate(-width, height); - canvas.rotate(180, width, 0); - } else { - canvas.translate(width, 0); - canvas.rotate(90); - } - - final boolean needsInvalidate = mEndEdge.draw(canvas); - canvas.restoreToCount(restoreCount); - return needsInvalidate; - } - - private void drawSelector(Canvas canvas) { - if (!mSelectorRect.isEmpty()) { - final Drawable selector = mSelector; - selector.setBounds(mSelectorRect); - selector.draw(canvas); - } - } - - private void useDefaultSelector() { - setSelector(getResources().getDrawable( - android.R.drawable.list_selector_background)); - } - - private boolean shouldShowSelector() { - return (hasFocus() && !isInTouchMode()) - || touchModeDrawsInPressedState(); - } - - private void positionSelector(int position, View selected) { - if (position != INVALID_POSITION) { - mSelectorPosition = position; - } - - mSelectorRect.set(selected.getLeft(), selected.getTop(), - selected.getRight(), selected.getBottom()); - - final boolean isChildViewEnabled = mIsChildViewEnabled; - if (selected.isEnabled() != isChildViewEnabled) { - mIsChildViewEnabled = !isChildViewEnabled; - - if (getSelectedItemPosition() != INVALID_POSITION) { - refreshDrawableState(); - } - } - } - - private void hideSelector() { - if (mSelectedPosition != INVALID_POSITION) { - if (mLayoutMode != LAYOUT_SPECIFIC) { - mResurrectToPosition = mSelectedPosition; - } - - if (mNextSelectedPosition >= 0 - && mNextSelectedPosition != mSelectedPosition) { - mResurrectToPosition = mNextSelectedPosition; - } - - setSelectedPositionInt(INVALID_POSITION); - setNextSelectedPositionInt(INVALID_POSITION); - - mSelectedStart = 0; - } - } - - private void setSelectedPositionInt(int position) { - mSelectedPosition = position; - mSelectedRowId = getItemIdAtPosition(position); - } - - private void setSelectionInt(int position) { - setNextSelectedPositionInt(position); - boolean awakeScrollbars = false; - - final int selectedPosition = mSelectedPosition; - if (selectedPosition >= 0) { - if (position == selectedPosition - 1) { - awakeScrollbars = true; - } else if (position == selectedPosition + 1) { - awakeScrollbars = true; - } - } - - layoutChildren(); - - if (awakeScrollbars) { - awakenScrollbarsInternal(); - } - } - - private void setNextSelectedPositionInt(int position) { - mNextSelectedPosition = position; - mNextSelectedRowId = getItemIdAtPosition(position); - - // If we are trying to sync to the selection, update that too - if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) { - mSyncPosition = position; - mSyncRowId = mNextSelectedRowId; - } - } - - private boolean touchModeDrawsInPressedState() { - switch (mTouchMode) { - case TOUCH_MODE_TAP: - case TOUCH_MODE_DONE_WAITING: - return true; - default: - return false; - } - } - - /** - * Sets the selector state to "pressed" and posts a CheckForKeyLongPress to - * see if this is a long press. - */ - private void keyPressed() { - if (!isEnabled() || !isClickable()) { - return; - } - - final Drawable selector = mSelector; - final Rect selectorRect = mSelectorRect; - - if (selector != null && (isFocused() || touchModeDrawsInPressedState()) - && !selectorRect.isEmpty()) { - - final View child = getChildAt(mSelectedPosition - mFirstPosition); - - if (child != null) { - if (child.hasFocusable()) { - return; - } - - child.setPressed(true); - } - - setPressed(true); - - final boolean longClickable = isLongClickable(); - final Drawable d = selector.getCurrent(); - if (d != null && d instanceof TransitionDrawable) { - if (longClickable) { - ((TransitionDrawable) d).startTransition(ViewConfiguration - .getLongPressTimeout()); - } else { - ((TransitionDrawable) d).resetTransition(); - } - } - - if (longClickable && !mDataChanged) { - if (mPendingCheckForKeyLongPress == null) { - mPendingCheckForKeyLongPress = new CheckForKeyLongPress(); - } - - mPendingCheckForKeyLongPress.rememberWindowAttachCount(); - postDelayed(mPendingCheckForKeyLongPress, - ViewConfiguration.getLongPressTimeout()); - } - } - } - - private void updateSelectorState() { - if (mSelector != null) { - if (shouldShowSelector()) { - mSelector.setState(getDrawableState()); - } else { - mSelector.setState(STATE_NOTHING); - } - } - } - - private void checkSelectionChanged() { - if ((mSelectedPosition != mOldSelectedPosition) - || (mSelectedRowId != mOldSelectedRowId)) { - selectionChanged(); - mOldSelectedPosition = mSelectedPosition; - mOldSelectedRowId = mSelectedRowId; - } - } - - private void selectionChanged() { - OnItemSelectedListener listener = getOnItemSelectedListener(); - if (listener == null) { - return; - } - - if (mInLayout || mBlockLayoutRequests) { - // If we are in a layout traversal, defer notification - // by posting. This ensures that the view tree is - // in a consistent state and is able to accommodate - // new layout or invalidate requests. - if (mSelectionNotifier == null) { - mSelectionNotifier = new SelectionNotifier(); - } - - post(mSelectionNotifier); - } else { - fireOnSelected(); - performAccessibilityActionsOnSelected(); - } - } - - private void fireOnSelected() { - OnItemSelectedListener listener = getOnItemSelectedListener(); - if (listener == null) { - return; - } - - final int selection = getSelectedItemPosition(); - if (selection >= 0) { - View v = getSelectedView(); - listener.onItemSelected(this, v, selection, - mAdapter.getItemId(selection)); - } else { - listener.onNothingSelected(this); - } - } - - private void performAccessibilityActionsOnSelected() { - final int position = getSelectedItemPosition(); - if (position >= 0) { - // We fire selection events here not in View - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); - } - } - - private int lookForSelectablePosition(int position) { - return lookForSelectablePosition(position, true); - } - - private int lookForSelectablePosition(int position, boolean lookDown) { - final ListAdapter adapter = mAdapter; - if (adapter == null || isInTouchMode()) { - return INVALID_POSITION; - } - - final int itemCount = mItemCount; - if (!mAreAllItemsSelectable) { - if (lookDown) { - position = Math.max(0, position); - while (position < itemCount && !adapter.isEnabled(position)) { - position++; - } - } else { - position = Math.min(position, itemCount - 1); - while (position >= 0 && !adapter.isEnabled(position)) { - position--; - } - } - - if (position < 0 || position >= itemCount) { - return INVALID_POSITION; - } - - return position; - } else { - if (position < 0 || position >= itemCount) { - return INVALID_POSITION; - } - - return position; - } - } - - /** - * @param direction - * either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN} or - * {@link View#FOCUS_LEFT} or {@link View#FOCUS_RIGHT} depending - * on the current view orientation. - * - * @return The position of the next selectable position of the views that - * are currently visible, taking into account the fact that there - * might be no selection. Returns {@link #INVALID_POSITION} if there - * is no selectable view on screen in the given direction. - */ - private int lookForSelectablePositionOnScreen(int direction) { - forceValidFocusDirection(direction); - - final int firstPosition = mFirstPosition; - final ListAdapter adapter = getAdapter(); - - if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) { - int startPos = (mSelectedPosition != INVALID_POSITION ? mSelectedPosition + 1 - : firstPosition); - - if (startPos >= adapter.getCount()) { - return INVALID_POSITION; - } - - if (startPos < firstPosition) { - startPos = firstPosition; - } - - final int lastVisiblePos = getLastVisiblePosition(); - - for (int pos = startPos; pos <= lastVisiblePos; pos++) { - if (adapter.isEnabled(pos) - && getChildAt(pos - firstPosition).getVisibility() == View.VISIBLE) { - return pos; - } - } - } else { - final int last = firstPosition + getChildCount() - 1; - - int startPos = (mSelectedPosition != INVALID_POSITION) ? mSelectedPosition - 1 - : firstPosition + getChildCount() - 1; - - if (startPos < 0 || startPos >= adapter.getCount()) { - return INVALID_POSITION; - } - - if (startPos > last) { - startPos = last; - } - - for (int pos = startPos; pos >= firstPosition; pos--) { - if (adapter.isEnabled(pos) - && getChildAt(pos - firstPosition).getVisibility() == View.VISIBLE) { - return pos; - } - } - } - - return INVALID_POSITION; - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - updateSelectorState(); - } - - @Override - protected int[] onCreateDrawableState(int extraSpace) { - // If the child view is enabled then do the default behavior. - if (mIsChildViewEnabled) { - // Common case - return super.onCreateDrawableState(extraSpace); - } - - // The selector uses this View's drawable state. The selected child view - // is disabled, so we need to remove the enabled state from the drawable - // states. - final int enabledState = ENABLED_STATE_SET[0]; - - // If we don't have any extra space, it will return one of the static - // state arrays, - // and clearing the enabled state on those arrays is a bad thing! If we - // specify - // we need extra space, it will create+copy into a new array that safely - // mutable. - int[] state = super.onCreateDrawableState(extraSpace + 1); - int enabledPos = -1; - for (int i = state.length - 1; i >= 0; i--) { - if (state[i] == enabledState) { - enabledPos = i; - break; - } - } - - // Remove the enabled state - if (enabledPos >= 0) { - System.arraycopy(state, enabledPos + 1, state, enabledPos, - state.length - enabledPos - 1); - } - - return state; - } - - @Override - protected boolean canAnimate() { - return (super.canAnimate() && mItemCount > 0); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - final boolean drawSelectorOnTop = mDrawSelectorOnTop; - if (!drawSelectorOnTop) { - drawSelector(canvas); - } - - super.dispatchDraw(canvas); - - if (drawSelectorOnTop) { - drawSelector(canvas); - } - } - - @Override - public void draw(Canvas canvas) { - super.draw(canvas); - - boolean needsInvalidate = false; - - if (mStartEdge != null) { - needsInvalidate |= drawStartEdge(canvas); - } - - if (mEndEdge != null) { - needsInvalidate |= drawEndEdge(canvas); - } - - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(this); - } - } - - @Override - public void requestLayout() { - if (!mInLayout && !mBlockLayoutRequests) { - super.requestLayout(); - } - } - - @Override - public View getSelectedView() { - if (mItemCount > 0 && mSelectedPosition >= 0) { - return getChildAt(mSelectedPosition - mFirstPosition); - } else { - return null; - } - } - - @Override - public void setSelection(int position) { - setSelectionFromOffset(position, 0); - } - - public void setSelectionFromOffset(int position, int offset) { - if (mAdapter == null) { - return; - } - - if (!isInTouchMode()) { - position = lookForSelectablePosition(position); - if (position >= 0) { - setNextSelectedPositionInt(position); - } - } else { - mResurrectToPosition = position; - } - - if (position >= 0) { - mLayoutMode = LAYOUT_SPECIFIC; - - if (mIsVertical) { - mSpecificStart = getPaddingTop() + offset; - } else { - mSpecificStart = getPaddingLeft() + offset; - } - - if (mNeedSync) { - mSyncPosition = position; - mSyncRowId = mAdapter.getItemId(position); - } - - requestLayout(); - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // Dispatch in the normal way - boolean handled = super.dispatchKeyEvent(event); - if (!handled) { - // If we didn't handle it... - final View focused = getFocusedChild(); - if (focused != null && event.getAction() == KeyEvent.ACTION_DOWN) { - // ... and our focused child didn't handle it - // ... give it to ourselves so we can scroll if necessary - handled = onKeyDown(event.getKeyCode(), event); - } - } - - return handled; - } - - @Override - protected void dispatchSetPressed(boolean pressed) { - // Don't dispatch setPressed to our children. We call setPressed on - // ourselves to - // get the selector in the right state, but we don't want to press each - // child. - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mSelector == null) { - useDefaultSelector(); - } - - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - int childWidth = 0; - int childHeight = 0; - - mItemCount = (mAdapter == null ? 0 : mAdapter.getCount()); - if (mItemCount > 0 - && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { - final View child = obtainView(0, mIsScrap); - - final int secondaryMeasureSpec = (mIsVertical ? widthMeasureSpec - : heightMeasureSpec); - - measureScrapChild(child, 0, secondaryMeasureSpec); - - childWidth = child.getMeasuredWidth(); - childHeight = child.getMeasuredHeight(); - - if (recycleOnMeasure()) { - mRecycler.addScrapView(child, -1); - } - } - - if (widthMode == MeasureSpec.UNSPECIFIED) { - widthSize = getPaddingLeft() + getPaddingRight() + childWidth; - if (mIsVertical) { - widthSize += getVerticalScrollbarWidth(); - } - } - - if (heightMode == MeasureSpec.UNSPECIFIED) { - heightSize = getPaddingTop() + getPaddingBottom() + childHeight; - if (!mIsVertical) { - heightSize += getHorizontalScrollbarHeight(); - } - } - - if (mIsVertical && heightMode == MeasureSpec.AT_MOST) { - heightSize = measureHeightOfChildren(widthMeasureSpec, 0, - NO_POSITION, heightSize, -1); - } - - if (!mIsVertical && widthMode == MeasureSpec.AT_MOST) { - widthSize = measureWidthOfChildren(heightMeasureSpec, 0, - NO_POSITION, widthSize, -1); - } - - setMeasuredDimension(widthSize, heightSize); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - mInLayout = true; - - if (changed) { - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - getChildAt(i).forceLayout(); - } - - mRecycler.markChildrenDirty(); - } - - layoutChildren(); - - mInLayout = false; - - final int width = r - l - getPaddingLeft() - getPaddingRight(); - final int height = b - t - getPaddingTop() - getPaddingBottom(); - - if (mStartEdge != null && mEndEdge != null) { - if (mIsVertical) { - mStartEdge.setSize(width, height); - mEndEdge.setSize(width, height); - } else { - mStartEdge.setSize(height, width); - mEndEdge.setSize(height, width); - } - } - } - - private void layoutChildren() { - if (getWidth() == 0 || getHeight() == 0) { - return; - } - - final boolean blockLayoutRequests = mBlockLayoutRequests; - if (!blockLayoutRequests) { - mBlockLayoutRequests = true; - } else { - return; - } - - try { - invalidate(); - - if (mAdapter == null) { - resetState(); - return; - } - - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - int childCount = getChildCount(); - int index = 0; - int delta = 0; - - View focusLayoutRestoreView = null; - - View selected = null; - View oldSelected = null; - View newSelected = null; - View oldFirstChild = null; - - switch (mLayoutMode) { - case LAYOUT_SET_SELECTION: - index = mNextSelectedPosition - mFirstPosition; - if (index >= 0 && index < childCount) { - newSelected = getChildAt(index); - } - - break; - - case LAYOUT_FORCE_TOP: - case LAYOUT_FORCE_BOTTOM: - case LAYOUT_SPECIFIC: - case LAYOUT_SYNC: - break; - - case LAYOUT_MOVE_SELECTION: - default: - // Remember the previously selected view - index = mSelectedPosition - mFirstPosition; - if (index >= 0 && index < childCount) { - oldSelected = getChildAt(index); - } - - // Remember the previous first child - oldFirstChild = getChildAt(0); - - if (mNextSelectedPosition >= 0) { - delta = mNextSelectedPosition - mSelectedPosition; - } - - // Caution: newSelected might be null - newSelected = getChildAt(index + delta); - } - - final boolean dataChanged = mDataChanged; - if (dataChanged) { - handleDataChanged(); - } - - // Handle the empty set by removing all views that are visible - // and calling it a day - if (mItemCount == 0) { - resetState(); - return; - } else if (mItemCount != mAdapter.getCount()) { - throw new IllegalStateException( - "The content of the adapter has changed but " - + "TwoWayView did not receive a notification. Make sure the content of " - + "your adapter is not modified from a background thread, but only " - + "from the UI thread. [in TwoWayView(" - + getId() + ", " + getClass() - + ") with Adapter(" + mAdapter.getClass() - + ")]"); - } - - setSelectedPositionInt(mNextSelectedPosition); - - // Reset the focus restoration - View focusLayoutRestoreDirectChild = null; - - // Pull all children into the RecycleBin. - // These views will be reused if possible - final int firstPosition = mFirstPosition; - final RecycleBin recycleBin = mRecycler; - - if (dataChanged) { - for (int i = 0; i < childCount; i++) { - recycleBin.addScrapView(getChildAt(i), firstPosition + i); - } - } else { - recycleBin.fillActiveViews(childCount, firstPosition); - } - - // Take focus back to us temporarily to avoid the eventual - // call to clear focus when removing the focused child below - // from messing things up when ViewAncestor assigns focus back - // to someone else. - final View focusedChild = getFocusedChild(); - if (focusedChild != null) { - // We can remember the focused view to restore after relayout if - // the - // data hasn't changed, or if the focused position is a header - // or footer. - if (!dataChanged) { - focusLayoutRestoreDirectChild = focusedChild; - - // Remember the specific view that had focus - focusLayoutRestoreView = findFocus(); - if (focusLayoutRestoreView != null) { - // Tell it we are going to mess with it - focusLayoutRestoreView.onStartTemporaryDetach(); - } - } - - requestFocus(); - } - - // FIXME: We need a way to save current accessibility focus here - // so that it can be restored after we re-attach the children on - // each - // layout round. - - detachAllViewsFromParent(); - - switch (mLayoutMode) { - case LAYOUT_SET_SELECTION: - if (newSelected != null) { - final int newSelectedStart = (mIsVertical ? newSelected - .getTop() : newSelected.getLeft()); - - selected = fillFromSelection(newSelectedStart, start, end); - } else { - selected = fillFromMiddle(start, end); - } - - break; - - case LAYOUT_SYNC: - selected = fillSpecific(mSyncPosition, mSpecificStart); - break; - - case LAYOUT_FORCE_BOTTOM: - selected = fillBefore(mItemCount - 1, end); - adjustViewsStartOrEnd(); - break; - - case LAYOUT_FORCE_TOP: - mFirstPosition = 0; - selected = fillFromOffset(start); - adjustViewsStartOrEnd(); - break; - - case LAYOUT_SPECIFIC: - selected = fillSpecific(reconcileSelectedPosition(), - mSpecificStart); - break; - - case LAYOUT_MOVE_SELECTION: - selected = moveSelection(oldSelected, newSelected, delta, - start, end); - break; - - default: - if (childCount == 0) { - final int position = lookForSelectablePosition(0); - setSelectedPositionInt(position); - selected = fillFromOffset(start); - } else { - if (mSelectedPosition >= 0 - && mSelectedPosition < mItemCount) { - int offset = start; - if (oldSelected != null) { - offset = (mIsVertical ? oldSelected.getTop() - : oldSelected.getLeft()); - } - selected = fillSpecific(mSelectedPosition, offset); - } else if (mFirstPosition < mItemCount) { - int offset = start; - if (oldFirstChild != null) { - offset = (mIsVertical ? oldFirstChild.getTop() - : oldFirstChild.getLeft()); - } - - selected = fillSpecific(mFirstPosition, offset); - } else { - selected = fillSpecific(0, start); - } - } - - break; - - } - - recycleBin.scrapActiveViews(); - - if (selected != null) { - if (mItemsCanFocus && hasFocus() && !selected.hasFocus()) { - final boolean focusWasTaken = (selected == focusLayoutRestoreDirectChild - && focusLayoutRestoreView != null && focusLayoutRestoreView - .requestFocus()) || selected.requestFocus(); - - if (!focusWasTaken) { - // Selected item didn't take focus, fine, but still want - // to make sure something else outside of the selected - // view - // has focus - final View focused = getFocusedChild(); - if (focused != null) { - focused.clearFocus(); - } - - positionSelector(INVALID_POSITION, selected); - } else { - selected.setSelected(false); - mSelectorRect.setEmpty(); - } - } else { - positionSelector(INVALID_POSITION, selected); - } - - mSelectedStart = (mIsVertical ? selected.getTop() : selected - .getLeft()); - } else { - if (mTouchMode > TOUCH_MODE_DOWN - && mTouchMode < TOUCH_MODE_DRAGGING) { - View child = getChildAt(mMotionPosition - mFirstPosition); - - if (child != null) { - positionSelector(mMotionPosition, child); - } - } else { - mSelectedStart = 0; - mSelectorRect.setEmpty(); - } - - // Even if there is not selected position, we may need to - // restore - // focus (i.e. something focusable in touch mode) - if (hasFocus() && focusLayoutRestoreView != null) { - focusLayoutRestoreView.requestFocus(); - } - } - - // Tell focus view we are done mucking with it, if it is still in - // our view hierarchy. - if (focusLayoutRestoreView != null - && focusLayoutRestoreView.getWindowToken() != null) { - focusLayoutRestoreView.onFinishTemporaryDetach(); - } - - mLayoutMode = LAYOUT_NORMAL; - mDataChanged = false; - mNeedSync = false; - - setNextSelectedPositionInt(mSelectedPosition); - if (mItemCount > 0) { - checkSelectionChanged(); - } - - invokeOnItemScrollListener(); - } finally { - if (!blockLayoutRequests) { - mBlockLayoutRequests = false; - mDataChanged = false; - } - } - } - - protected boolean recycleOnMeasure() { - return true; - } - - private void offsetChildren(int offset) { - final int childCount = getChildCount(); - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - - if (mIsVertical) { - child.offsetTopAndBottom(offset); - } else { - child.offsetLeftAndRight(offset); - } - } - } - - private View moveSelection(View oldSelected, View newSelected, int delta, - int start, int end) { - final int selectedPosition = mSelectedPosition; - - final int oldSelectedStart = (mIsVertical ? oldSelected.getTop() - : oldSelected.getLeft()); - final int oldSelectedEnd = (mIsVertical ? oldSelected.getBottom() - : oldSelected.getRight()); - - View selected = null; - - if (delta > 0) { - /* - * Case 1: Scrolling down. - */ - - /* - * Before After | | | | +-------+ +-------+ | A | | A | | 1 | => - * +-------+ +-------+ | B | | B | | 2 | +-------+ +-------+ | | | | - * - * Try to keep the top of the previously selected item where it was. - * oldSelected = A selected = B - */ - - // Put oldSelected (A) where it belongs - oldSelected = makeAndAddView(selectedPosition - 1, - oldSelectedStart, true, false); - - final int itemMargin = mItemMargin; - - // Now put the new selection (B) below that - selected = makeAndAddView(selectedPosition, oldSelectedEnd - + itemMargin, true, true); - - final int selectedStart = (mIsVertical ? selected.getTop() - : selected.getLeft()); - final int selectedEnd = (mIsVertical ? selected.getBottom() - : selected.getRight()); - - // Some of the newly selected item extends below the bottom of the - // list - if (selectedEnd > end) { - // Find space available above the selection into which we can - // scroll upwards - final int spaceBefore = selectedStart - start; - - // Find space required to bring the bottom of the selected item - // fully into view - final int spaceAfter = selectedEnd - end; - - // Don't scroll more than half the size of the list - final int halfSpace = (end - start) / 2; - int offset = Math.min(spaceBefore, spaceAfter); - offset = Math.min(offset, halfSpace); - - if (mIsVertical) { - oldSelected.offsetTopAndBottom(-offset); - selected.offsetTopAndBottom(-offset); - } else { - oldSelected.offsetLeftAndRight(-offset); - selected.offsetLeftAndRight(-offset); - } - } - - // Fill in views before and after - fillBefore(mSelectedPosition - 2, selectedStart - itemMargin); - adjustViewsStartOrEnd(); - fillAfter(mSelectedPosition + 1, selectedEnd + itemMargin); - } else if (delta < 0) { - /* - * Case 2: Scrolling up. - */ - - /* - * Before After | | | | +-------+ +-------+ | A | | A | +-------+ => - * | 1 | | B | +-------+ | 2 | | B | +-------+ +-------+ | | | | - * - * Try to keep the top of the item about to become selected where it - * was. newSelected = A olSelected = B - */ - - if (newSelected != null) { - // Try to position the top of newSel (A) where it was before it - // was selected - final int newSelectedStart = (mIsVertical ? newSelected - .getTop() : newSelected.getLeft()); - selected = makeAndAddView(selectedPosition, newSelectedStart, - true, true); - } else { - // If (A) was not on screen and so did not have a view, position - // it above the oldSelected (B) - selected = makeAndAddView(selectedPosition, oldSelectedStart, - false, true); - } - - final int selectedStart = (mIsVertical ? selected.getTop() - : selected.getLeft()); - final int selectedEnd = (mIsVertical ? selected.getBottom() - : selected.getRight()); - - // Some of the newly selected item extends above the top of the list - if (selectedStart < start) { - // Find space required to bring the top of the selected item - // fully into view - final int spaceBefore = start - selectedStart; - - // Find space available below the selection into which we can - // scroll downwards - final int spaceAfter = end - selectedEnd; - - // Don't scroll more than half the height of the list - final int halfSpace = (end - start) / 2; - int offset = Math.min(spaceBefore, spaceAfter); - offset = Math.min(offset, halfSpace); - - if (mIsVertical) { - selected.offsetTopAndBottom(offset); - } else { - selected.offsetLeftAndRight(offset); - } - } - - // Fill in views above and below - fillBeforeAndAfter(selected, selectedPosition); - } else { - /* - * Case 3: Staying still - */ - - selected = makeAndAddView(selectedPosition, oldSelectedStart, true, - true); - - final int selectedStart = (mIsVertical ? selected.getTop() - : selected.getLeft()); - final int selectedEnd = (mIsVertical ? selected.getBottom() - : selected.getRight()); - - // We're staying still... - if (oldSelectedStart < start) { - // ... but the top of the old selection was off screen. - // (This can happen if the data changes size out from under us) - int newEnd = selectedEnd; - if (newEnd < start + 20) { - // Not enough visible -- bring it onscreen - if (mIsVertical) { - selected.offsetTopAndBottom(start - selectedStart); - } else { - selected.offsetLeftAndRight(start - selectedStart); - } - } - } - - // Fill in views above and below - fillBeforeAndAfter(selected, selectedPosition); - } - - return selected; - } - - void confirmCheckedPositionsById() { - // Clear out the positional check states, we'll rebuild it below from - // IDs. - mCheckStates.clear(); - - for (int checkedIndex = 0; checkedIndex < mCheckedIdStates.size(); checkedIndex++) { - final long id = mCheckedIdStates.keyAt(checkedIndex); - final int lastPos = mCheckedIdStates.valueAt(checkedIndex); - - final long lastPosId = mAdapter.getItemId(lastPos); - if (id != lastPosId) { - // Look around to see if the ID is nearby. If not, uncheck it. - final int start = Math.max(0, lastPos - - CHECK_POSITION_SEARCH_DISTANCE); - final int end = Math.min(lastPos - + CHECK_POSITION_SEARCH_DISTANCE, mItemCount); - boolean found = false; - - for (int searchPos = start; searchPos < end; searchPos++) { - final long searchId = mAdapter.getItemId(searchPos); - if (id == searchId) { - found = true; - mCheckStates.put(searchPos, true); - mCheckedIdStates.setValueAt(checkedIndex, searchPos); - break; - } - } - - if (!found) { - mCheckedIdStates.delete(id); - checkedIndex--; - mCheckedItemCount--; - } - } else { - mCheckStates.put(lastPos, true); - } - } - } - - private void handleDataChanged() { - if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mAdapter != null - && mAdapter.hasStableIds()) { - confirmCheckedPositionsById(); - } - - mRecycler.clearTransientStateViews(); - - final int itemCount = mItemCount; - if (itemCount > 0) { - int newPos; - int selectablePos; - - // Find the row we are supposed to sync to - if (mNeedSync) { - // Update this first, since setNextSelectedPositionInt inspects - // it - mNeedSync = false; - mPendingSync = null; - - switch (mSyncMode) { - case SYNC_SELECTED_POSITION: - if (isInTouchMode()) { - // We saved our state when not in touch mode. (We know - // this because - // mSyncMode is SYNC_SELECTED_POSITION.) Now we are - // trying to - // restore in touch mode. Just leave mSyncPosition as it - // is (possibly - // adjusting if the available range changed) and return. - mLayoutMode = LAYOUT_SYNC; - mSyncPosition = Math.min(Math.max(0, mSyncPosition), - itemCount - 1); - - return; - } else { - // See if we can find a position in the new data with - // the same - // id as the old selection. This will change - // mSyncPosition. - newPos = findSyncPosition(); - if (newPos >= 0) { - // Found it. Now verify that new selection is still - // selectable - selectablePos = lookForSelectablePosition(newPos, - true); - if (selectablePos == newPos) { - // Same row id is selected - mSyncPosition = newPos; - - if (mSyncHeight == getHeight()) { - // If we are at the same height as when we - // saved state, try - // to restore the scroll position too. - mLayoutMode = LAYOUT_SYNC; - } else { - // We are not the same height as when the - // selection was saved, so - // don't try to restore the exact position - mLayoutMode = LAYOUT_SET_SELECTION; - } - - // Restore selection - setNextSelectedPositionInt(newPos); - return; - } - } - } - break; - - case SYNC_FIRST_POSITION: - // Leave mSyncPosition as it is -- just pin to available - // range - mLayoutMode = LAYOUT_SYNC; - mSyncPosition = Math.min(Math.max(0, mSyncPosition), - itemCount - 1); - - return; - } - } - - if (!isInTouchMode()) { - // We couldn't find matching data -- try to use the same - // position - newPos = getSelectedItemPosition(); - - // Pin position to the available range - if (newPos >= itemCount) { - newPos = itemCount - 1; - } - if (newPos < 0) { - newPos = 0; - } - - // Make sure we select something selectable -- first look down - selectablePos = lookForSelectablePosition(newPos, true); - - if (selectablePos >= 0) { - setNextSelectedPositionInt(selectablePos); - return; - } else { - // Looking down didn't work -- try looking up - selectablePos = lookForSelectablePosition(newPos, false); - if (selectablePos >= 0) { - setNextSelectedPositionInt(selectablePos); - return; - } - } - } else { - // We already know where we want to resurrect the selection - if (mResurrectToPosition >= 0) { - return; - } - } - } - - // Nothing is selected. Give up and reset everything. - mLayoutMode = LAYOUT_FORCE_TOP; - mSelectedPosition = INVALID_POSITION; - mSelectedRowId = INVALID_ROW_ID; - mNextSelectedPosition = INVALID_POSITION; - mNextSelectedRowId = INVALID_ROW_ID; - mNeedSync = false; - mPendingSync = null; - mSelectorPosition = INVALID_POSITION; - - checkSelectionChanged(); - } - - private int reconcileSelectedPosition() { - int position = mSelectedPosition; - if (position < 0) { - position = mResurrectToPosition; - } - - position = Math.max(0, position); - position = Math.min(position, mItemCount - 1); - - return position; - } - - boolean resurrectSelection() { - final int childCount = getChildCount(); - if (childCount <= 0) { - return false; - } - - int selectedStart = 0; - int selectedPosition; - - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - final int firstPosition = mFirstPosition; - final int toPosition = mResurrectToPosition; - boolean down = true; - - if (toPosition >= firstPosition - && toPosition < firstPosition + childCount) { - selectedPosition = toPosition; - - final View selected = getChildAt(selectedPosition - mFirstPosition); - selectedStart = (mIsVertical ? selected.getTop() : selected - .getLeft()); - } else if (toPosition < firstPosition) { - // Default to selecting whatever is first - selectedPosition = firstPosition; - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final int childStart = (mIsVertical ? child.getTop() : child - .getLeft()); - - if (i == 0) { - // Remember the position of the first item - selectedStart = childStart; - } - - if (childStart >= start) { - // Found a view whose top is fully visible - selectedPosition = firstPosition + i; - selectedStart = childStart; - break; - } - } - } else { - selectedPosition = firstPosition + childCount - 1; - down = false; - - for (int i = childCount - 1; i >= 0; i--) { - final View child = getChildAt(i); - final int childStart = (mIsVertical ? child.getTop() : child - .getLeft()); - final int childEnd = (mIsVertical ? child.getBottom() : child - .getRight()); - - if (i == childCount - 1) { - selectedStart = childStart; - } - - if (childEnd <= end) { - selectedPosition = firstPosition + i; - selectedStart = childStart; - break; - } - } - } - - mResurrectToPosition = INVALID_POSITION; - mTouchMode = TOUCH_MODE_REST; - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - - mSpecificStart = selectedStart; - - selectedPosition = lookForSelectablePosition(selectedPosition, down); - if (selectedPosition >= firstPosition - && selectedPosition <= getLastVisiblePosition()) { - mLayoutMode = LAYOUT_SPECIFIC; - updateSelectorState(); - setSelectionInt(selectedPosition); - invokeOnItemScrollListener(); - } else { - selectedPosition = INVALID_POSITION; - } - - return selectedPosition >= 0; - } - - /** - * If there is a selection returns false. Otherwise resurrects the selection - * and returns true if resurrected. - */ - boolean resurrectSelectionIfNeeded() { - if (mSelectedPosition < 0 && resurrectSelection()) { - updateSelectorState(); - return true; - } - - return false; - } - - private int getChildWidthMeasureSpec(LayoutParams lp) { - if (!mIsVertical && lp.width == LayoutParams.WRAP_CONTENT) { - return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } else if (mIsVertical) { - final int maxWidth = getWidth() - getPaddingLeft() - - getPaddingRight(); - return MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY); - } else { - return MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); - } - } - - private int getChildHeightMeasureSpec(LayoutParams lp) { - if (mIsVertical && lp.height == LayoutParams.WRAP_CONTENT) { - return MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); - } else if (!mIsVertical) { - final int maxHeight = getHeight() - getPaddingTop() - - getPaddingBottom(); - return MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY); - } else { - return MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - } - } - - private void measureChild(View child) { - measureChild(child, (LayoutParams) child.getLayoutParams()); - } - - private void measureChild(View child, LayoutParams lp) { - final int widthSpec = getChildWidthMeasureSpec(lp); - final int heightSpec = getChildHeightMeasureSpec(lp); - child.measure(widthSpec, heightSpec); - } - - private void relayoutMeasuredChild(View child) { - final int w = child.getMeasuredWidth(); - final int h = child.getMeasuredHeight(); - - final int childLeft = getPaddingLeft(); - final int childRight = childLeft + w; - final int childTop = child.getTop(); - final int childBottom = childTop + h; - - child.layout(childLeft, childTop, childRight, childBottom); - } - - private void measureScrapChild(View scrapChild, int position, - int secondaryMeasureSpec) { - LayoutParams lp = (LayoutParams) scrapChild.getLayoutParams(); - if (lp == null) { - lp = generateDefaultLayoutParams(); - scrapChild.setLayoutParams(lp); - } - - lp.viewType = mAdapter.getItemViewType(position); - lp.forceAdd = true; - - final int widthMeasureSpec; - final int heightMeasureSpec; - if (mIsVertical) { - widthMeasureSpec = secondaryMeasureSpec; - heightMeasureSpec = getChildHeightMeasureSpec(lp); - } else { - widthMeasureSpec = getChildWidthMeasureSpec(lp); - heightMeasureSpec = secondaryMeasureSpec; - } - - scrapChild.measure(widthMeasureSpec, heightMeasureSpec); - } - - /** - * Measures the height of the given range of children (inclusive) and - * returns the height with this TwoWayView's padding and item margin heights - * included. If maxHeight is provided, the measuring will stop when the - * current height reaches maxHeight. - * - * @param widthMeasureSpec - * The width measure spec to be given to a child's - * {@link View#measure(int, int)}. - * @param startPosition - * The position of the first child to be shown. - * @param endPosition - * The (inclusive) position of the last child to be shown. - * Specify {@link #NO_POSITION} if the last child should be the - * last available child from the adapter. - * @param maxHeight - * The maximum height that will be returned (if all the children - * don't fit in this value, this value will be returned). - * @param disallowPartialChildPosition - * In general, whether the returned height should only contain - * entire children. This is more powerful--it is the first - * inclusive position at which partial children will not be - * allowed. Example: it looks nice to have at least 3 completely - * visible children, and in portrait this will most likely fit; - * but in landscape there could be times when even 2 children can - * not be completely shown, so a value of 2 (remember, inclusive) - * would be good (assuming startPosition is 0). - * @return The height of this TwoWayView with the given children. - */ - private int measureHeightOfChildren(int widthMeasureSpec, - int startPosition, int endPosition, final int maxHeight, - int disallowPartialChildPosition) { - - final int paddingTop = getPaddingTop(); - final int paddingBottom = getPaddingBottom(); - - final ListAdapter adapter = mAdapter; - if (adapter == null) { - return paddingTop + paddingBottom; - } - - // Include the padding of the list - int returnedHeight = paddingTop + paddingBottom; - final int itemMargin = mItemMargin; - - // The previous height value that was less than maxHeight and contained - // no partial children - int prevHeightWithoutPartialChild = 0; - int i; - View child; - - // mItemCount - 1 since endPosition parameter is inclusive - endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 - : endPosition; - final RecycleBin recycleBin = mRecycler; - final boolean shouldRecycle = recycleOnMeasure(); - final boolean[] isScrap = mIsScrap; - - for (i = startPosition; i <= endPosition; ++i) { - child = obtainView(i, isScrap); - - measureScrapChild(child, i, widthMeasureSpec); - - if (i > 0) { - // Count the item margin for all but one child - returnedHeight += itemMargin; - } - - // Recycle the view before we possibly return from the method - if (shouldRecycle) { - recycleBin.addScrapView(child, -1); - } - - returnedHeight += child.getMeasuredHeight(); - - if (returnedHeight >= maxHeight) { - // We went over, figure out which height to return. If - // returnedHeight > maxHeight, - // then the i'th position did not fit completely. - return (disallowPartialChildPosition >= 0) // Disallowing is - // enabled (> -1) - && (i > disallowPartialChildPosition) // We've past the - // min pos - && (prevHeightWithoutPartialChild > 0) // We have a prev - // height - && (returnedHeight != maxHeight) // i'th child did not - // fit completely - ? prevHeightWithoutPartialChild : maxHeight; - } - - if ((disallowPartialChildPosition >= 0) - && (i >= disallowPartialChildPosition)) { - prevHeightWithoutPartialChild = returnedHeight; - } - } - - // At this point, we went through the range of children, and they each - // completely fit, so return the returnedHeight - return returnedHeight; - } - - /** - * Measures the width of the given range of children (inclusive) and returns - * the width with this TwoWayView's padding and item margin widths included. - * If maxWidth is provided, the measuring will stop when the current width - * reaches maxWidth. - * - * @param heightMeasureSpec - * The height measure spec to be given to a child's - * {@link View#measure(int, int)}. - * @param startPosition - * The position of the first child to be shown. - * @param endPosition - * The (inclusive) position of the last child to be shown. - * Specify {@link #NO_POSITION} if the last child should be the - * last available child from the adapter. - * @param maxWidth - * The maximum width that will be returned (if all the children - * don't fit in this value, this value will be returned). - * @param disallowPartialChildPosition - * In general, whether the returned width should only contain - * entire children. This is more powerful--it is the first - * inclusive position at which partial children will not be - * allowed. Example: it looks nice to have at least 3 completely - * visible children, and in portrait this will most likely fit; - * but in landscape there could be times when even 2 children can - * not be completely shown, so a value of 2 (remember, inclusive) - * would be good (assuming startPosition is 0). - * @return The width of this TwoWayView with the given children. - */ - private int measureWidthOfChildren(int heightMeasureSpec, - int startPosition, int endPosition, final int maxWidth, - int disallowPartialChildPosition) { - - final int paddingLeft = getPaddingLeft(); - final int paddingRight = getPaddingRight(); - - final ListAdapter adapter = mAdapter; - if (adapter == null) { - return paddingLeft + paddingRight; - } - - // Include the padding of the list - int returnedWidth = paddingLeft + paddingRight; - final int itemMargin = mItemMargin; - - // The previous height value that was less than maxHeight and contained - // no partial children - int prevWidthWithoutPartialChild = 0; - int i; - View child; - - // mItemCount - 1 since endPosition parameter is inclusive - endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 - : endPosition; - final RecycleBin recycleBin = mRecycler; - final boolean shouldRecycle = recycleOnMeasure(); - final boolean[] isScrap = mIsScrap; - - for (i = startPosition; i <= endPosition; ++i) { - child = obtainView(i, isScrap); - - measureScrapChild(child, i, heightMeasureSpec); - - if (i > 0) { - // Count the item margin for all but one child - returnedWidth += itemMargin; - } - - // Recycle the view before we possibly return from the method - if (shouldRecycle) { - recycleBin.addScrapView(child, -1); - } - - returnedWidth += child.getMeasuredHeight(); - - if (returnedWidth >= maxWidth) { - // We went over, figure out which width to return. If - // returnedWidth > maxWidth, - // then the i'th position did not fit completely. - return (disallowPartialChildPosition >= 0) // Disallowing is - // enabled (> -1) - && (i > disallowPartialChildPosition) // We've past the - // min pos - && (prevWidthWithoutPartialChild > 0) // We have a prev - // width - && (returnedWidth != maxWidth) // i'th child did not fit - // completely - ? prevWidthWithoutPartialChild : maxWidth; - } - - if ((disallowPartialChildPosition >= 0) - && (i >= disallowPartialChildPosition)) { - prevWidthWithoutPartialChild = returnedWidth; - } - } - - // At this point, we went through the range of children, and they each - // completely fit, so return the returnedWidth - return returnedWidth; - } - - private View makeAndAddView(int position, int offset, boolean flow, - boolean selected) { - final int top; - final int left; - - if (mIsVertical) { - top = offset; - left = getPaddingLeft(); - } else { - top = getPaddingTop(); - left = offset; - } - - if (!mDataChanged) { - // Try to use an existing view for this position - final View activeChild = mRecycler.getActiveView(position); - if (activeChild != null) { - // Found it -- we're using an existing child - // This just needs to be positioned - setupChild(activeChild, position, top, left, flow, selected, - true); - - return activeChild; - } - } - - // Make a new view for this position, or convert an unused view if - // possible - final View child = obtainView(position, mIsScrap); - - // This needs to be positioned and measured - setupChild(child, position, top, left, flow, selected, mIsScrap[0]); - - return child; - } - - @TargetApi(11) - private void setupChild(View child, int position, int top, int left, - boolean flow, boolean selected, boolean recycled) { - final boolean isSelected = selected && shouldShowSelector(); - final boolean updateChildSelected = isSelected != child.isSelected(); - final int touchMode = mTouchMode; - - final boolean isPressed = touchMode > TOUCH_MODE_DOWN - && touchMode < TOUCH_MODE_DRAGGING - && mMotionPosition == position; - - final boolean updateChildPressed = isPressed != child.isPressed(); - final boolean needToMeasure = !recycled || updateChildSelected - || child.isLayoutRequested(); - - // Respect layout params that are already in the view. Otherwise make - // some up... - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp == null) { - lp = generateDefaultLayoutParams(); - } - - lp.viewType = mAdapter.getItemViewType(position); - - if (recycled && !lp.forceAdd) { - attachViewToParent(child, (flow ? -1 : 0), lp); - } else { - lp.forceAdd = false; - addViewInLayout(child, (flow ? -1 : 0), lp, true); - } - - if (updateChildSelected) { - child.setSelected(isSelected); - } - - if (updateChildPressed) { - child.setPressed(isPressed); - } - - if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mCheckStates != null) { - if (child instanceof Checkable) { - ((Checkable) child).setChecked(mCheckStates.get(position)); - } else if (getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { - child.setActivated(mCheckStates.get(position)); - } - } - - if (needToMeasure) { - measureChild(child, lp); - } else { - cleanupLayoutState(child); - } - - final int w = child.getMeasuredWidth(); - final int h = child.getMeasuredHeight(); - - final int childTop = (mIsVertical && !flow ? top - h : top); - final int childLeft = (!mIsVertical && !flow ? left - w : left); - - if (needToMeasure) { - final int childRight = childLeft + w; - final int childBottom = childTop + h; - - child.layout(childLeft, childTop, childRight, childBottom); - } else { - child.offsetLeftAndRight(childLeft - child.getLeft()); - child.offsetTopAndBottom(childTop - child.getTop()); - } - } - - void fillGap(boolean down) { - final int childCount = getChildCount(); - - if (down) { - final int paddingStart = (mIsVertical ? getPaddingTop() - : getPaddingLeft()); - - final int lastEnd; - if (mIsVertical) { - lastEnd = getChildAt(childCount - 1).getBottom(); - } else { - lastEnd = getChildAt(childCount - 1).getRight(); - } - - final int offset = (childCount > 0 ? lastEnd + mItemMargin - : paddingStart); - fillAfter(mFirstPosition + childCount, offset); - correctTooHigh(getChildCount()); - } else { - final int end; - final int firstStart; - - if (mIsVertical) { - end = getHeight() - getPaddingBottom(); - firstStart = getChildAt(0).getTop(); - } else { - end = getWidth() - getPaddingRight(); - firstStart = getChildAt(0).getLeft(); - } - - final int offset = (childCount > 0 ? firstStart - mItemMargin : end); - fillBefore(mFirstPosition - 1, offset); - correctTooLow(getChildCount()); - } - } - - private View fillBefore(int pos, int nextOffset) { - View selectedView = null; - - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - - while (nextOffset > start && pos >= 0) { - boolean isSelected = (pos == mSelectedPosition); - View child = makeAndAddView(pos, nextOffset, false, isSelected); - - if (mIsVertical) { - nextOffset = child.getTop() - mItemMargin; - } else { - nextOffset = child.getLeft() - mItemMargin; - } - - if (isSelected) { - selectedView = child; - } - - pos--; - } - - mFirstPosition = pos + 1; - - return selectedView; - } - - private View fillAfter(int pos, int nextOffset) { - View selectedView = null; - - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - while (nextOffset < end && pos < mItemCount) { - boolean selected = (pos == mSelectedPosition); - - View child = makeAndAddView(pos, nextOffset, true, selected); - - if (mIsVertical) { - nextOffset = child.getBottom() + mItemMargin; - } else { - nextOffset = child.getRight() + mItemMargin; - } - - if (selected) { - selectedView = child; - } - - pos++; - } - - return selectedView; - } - - private View fillSpecific(int position, int offset) { - final boolean tempIsSelected = (position == mSelectedPosition); - View temp = makeAndAddView(position, offset, true, tempIsSelected); - - // Possibly changed again in fillBefore if we add rows above this one. - mFirstPosition = position; - - final int itemMargin = mItemMargin; - - final int offsetBefore; - if (mIsVertical) { - offsetBefore = temp.getTop() - itemMargin; - } else { - offsetBefore = temp.getLeft() - itemMargin; - } - final View before = fillBefore(position - 1, offsetBefore); - - // This will correct for the top of the first view not touching the top - // of the list - adjustViewsStartOrEnd(); - - final int offsetAfter; - if (mIsVertical) { - offsetAfter = temp.getBottom() + itemMargin; - } else { - offsetAfter = temp.getRight() + itemMargin; - } - final View after = fillAfter(position + 1, offsetAfter); - - final int childCount = getChildCount(); - if (childCount > 0) { - correctTooHigh(childCount); - } - - if (tempIsSelected) { - return temp; - } else if (before != null) { - return before; - } else { - return after; - } - } - - private View fillFromOffset(int nextOffset) { - mFirstPosition = Math.min(mFirstPosition, mSelectedPosition); - mFirstPosition = Math.min(mFirstPosition, mItemCount - 1); - - if (mFirstPosition < 0) { - mFirstPosition = 0; - } - - return fillAfter(mFirstPosition, nextOffset); - } - - private View fillFromMiddle(int start, int end) { - final int size = end - start; - int position = reconcileSelectedPosition(); - - View selected = makeAndAddView(position, start, true, true); - mFirstPosition = position; - - if (mIsVertical) { - int selectedHeight = selected.getMeasuredHeight(); - if (selectedHeight <= size) { - selected.offsetTopAndBottom((size - selectedHeight) / 2); - } - } else { - int selectedWidth = selected.getMeasuredWidth(); - if (selectedWidth <= size) { - selected.offsetLeftAndRight((size - selectedWidth) / 2); - } - } - - fillBeforeAndAfter(selected, position); - correctTooHigh(getChildCount()); - - return selected; - } - - private void fillBeforeAndAfter(View selected, int position) { - final int itemMargin = mItemMargin; - - final int offsetBefore; - if (mIsVertical) { - offsetBefore = selected.getTop() - itemMargin; - } else { - offsetBefore = selected.getLeft() - itemMargin; - } - - fillBefore(position - 1, offsetBefore); - - adjustViewsStartOrEnd(); - - final int offsetAfter; - if (mIsVertical) { - offsetAfter = selected.getBottom() + itemMargin; - } else { - offsetAfter = selected.getRight() + itemMargin; - } - - fillAfter(position + 1, offsetAfter); - } - - private View fillFromSelection(int selectedTop, int start, int end) { - final int selectedPosition = mSelectedPosition; - View selected; - - selected = makeAndAddView(selectedPosition, selectedTop, true, true); - - final int selectedStart = (mIsVertical ? selected.getTop() : selected - .getLeft()); - final int selectedEnd = (mIsVertical ? selected.getBottom() : selected - .getRight()); - - // Some of the newly selected item extends below the bottom of the list - if (selectedEnd > end) { - // Find space available above the selection into which we can scroll - // upwards - final int spaceAbove = selectedStart - start; - - // Find space required to bring the bottom of the selected item - // fully into view - final int spaceBelow = selectedEnd - end; - - final int offset = Math.min(spaceAbove, spaceBelow); - - // Now offset the selected item to get it into view - selected.offsetTopAndBottom(-offset); - } else if (selectedStart < start) { - // Find space required to bring the top of the selected item fully - // into view - final int spaceAbove = start - selectedStart; - - // Find space available below the selection into which we can scroll - // downwards - final int spaceBelow = end - selectedEnd; - - final int offset = Math.min(spaceAbove, spaceBelow); - - // Offset the selected item to get it into view - selected.offsetTopAndBottom(offset); - } - - // Fill in views above and below - fillBeforeAndAfter(selected, selectedPosition); - correctTooHigh(getChildCount()); - - return selected; - } - - private void correctTooHigh(int childCount) { - // First see if the last item is visible. If it is not, it is OK for the - // top of the list to be pushed up. - final int lastPosition = mFirstPosition + childCount - 1; - if (lastPosition != mItemCount - 1 || childCount == 0) { - return; - } - - // Get the last child ... - final View lastChild = getChildAt(childCount - 1); - - // ... and its end edge - final int lastEnd; - if (mIsVertical) { - lastEnd = lastChild.getBottom(); - } else { - lastEnd = lastChild.getRight(); - } - - // This is bottom of our drawable area - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - final int end = (mIsVertical ? getHeight() - getPaddingBottom() - : getWidth() - getPaddingRight()); - - // This is how far the end edge of the last view is from the end of the - // drawable area - int endOffset = end - lastEnd; - - View firstChild = getChildAt(0); - int firstStart = (mIsVertical ? firstChild.getTop() : firstChild - .getLeft()); - - // Make sure we are 1) Too high, and 2) Either there are more rows above - // the - // first row or the first row is scrolled off the top of the drawable - // area - if (endOffset > 0 && (mFirstPosition > 0 || firstStart < start)) { - if (mFirstPosition == 0) { - // Don't pull the top too far down - endOffset = Math.min(endOffset, start - firstStart); - } - - // Move everything down - offsetChildren(endOffset); - - if (mFirstPosition > 0) { - firstStart = (mIsVertical ? firstChild.getTop() : firstChild - .getLeft()); - - // Fill the gap that was opened above mFirstPosition with more - // rows, if - // possible - fillBefore(mFirstPosition - 1, firstStart - mItemMargin); - - // Close up the remaining gap - adjustViewsStartOrEnd(); - } - } - } - - private void correctTooLow(int childCount) { - // First see if the first item is visible. If it is not, it is OK for - // the - // bottom of the list to be pushed down. - if (mFirstPosition != 0 || childCount == 0) { - return; - } - - final View first = getChildAt(0); - final int firstStart = (mIsVertical ? first.getTop() : first.getLeft()); - - final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft()); - - final int end; - if (mIsVertical) { - end = getHeight() - getPaddingBottom(); - } else { - end = getWidth() - getPaddingRight(); - } - - // This is how far the start edge of the first view is from the start of - // the - // drawable area - int startOffset = firstStart - start; - - View last = getChildAt(childCount - 1); - int lastEnd = (mIsVertical ? last.getBottom() : last.getRight()); - - int lastPosition = mFirstPosition + childCount - 1; - - // Make sure we are 1) Too low, and 2) Either there are more - // columns/rows below the - // last column/row or the last column/row is scrolled off the end of the - // drawable area - if (startOffset > 0) { - if (lastPosition < mItemCount - 1 || lastEnd > end) { - if (lastPosition == mItemCount - 1) { - // Don't pull the bottom too far up - startOffset = Math.min(startOffset, lastEnd - end); - } - - // Move everything up - offsetChildren(-startOffset); - - if (lastPosition < mItemCount - 1) { - lastEnd = (mIsVertical ? last.getBottom() : last.getRight()); - - // Fill the gap that was opened below the last position with - // more rows, if - // possible - fillAfter(lastPosition + 1, lastEnd + mItemMargin); - - // Close up the remaining gap - adjustViewsStartOrEnd(); - } - } else if (lastPosition == mItemCount - 1) { - adjustViewsStartOrEnd(); - } - } - } - - private void adjustViewsStartOrEnd() { - if (getChildCount() == 0) { - return; - } - - final View firstChild = getChildAt(0); - - int delta; - if (mIsVertical) { - delta = firstChild.getTop() - getPaddingTop() - mItemMargin; - } else { - delta = firstChild.getLeft() - getPaddingLeft() - mItemMargin; - } - - if (delta < 0) { - // We only are looking to see if we are too low, not too high - delta = 0; - } - - if (delta != 0) { - offsetChildren(-delta); - } - } - - @TargetApi(14) - private SparseBooleanArray cloneCheckStates() { - if (mCheckStates == null) { - return null; - } - - SparseBooleanArray checkedStates; - - if (Build.VERSION.SDK_INT >= 14) { - checkedStates = mCheckStates.clone(); - } else { - checkedStates = new SparseBooleanArray(); - - for (int i = 0; i < mCheckStates.size(); i++) { - checkedStates.put(mCheckStates.keyAt(i), - mCheckStates.valueAt(i)); - } - } - - return checkedStates; - } - - private int findSyncPosition() { - int itemCount = mItemCount; - - if (itemCount == 0) { - return INVALID_POSITION; - } - - final long idToMatch = mSyncRowId; - - // If there isn't a selection don't hunt for it - if (idToMatch == INVALID_ROW_ID) { - return INVALID_POSITION; - } - - // Pin seed to reasonable values - int seed = mSyncPosition; - seed = Math.max(0, seed); - seed = Math.min(itemCount - 1, seed); - - long endTime = SystemClock.uptimeMillis() + SYNC_MAX_DURATION_MILLIS; - - long rowId; - - // first position scanned so far - int first = seed; - - // last position scanned so far - int last = seed; - - // True if we should move down on the next iteration - boolean next = false; - - // True when we have looked at the first item in the data - boolean hitFirst; - - // True when we have looked at the last item in the data - boolean hitLast; - - // Get the item ID locally (instead of getItemIdAtPosition), so - // we need the adapter - final ListAdapter adapter = mAdapter; - if (adapter == null) { - return INVALID_POSITION; - } - - while (SystemClock.uptimeMillis() <= endTime) { - rowId = adapter.getItemId(seed); - if (rowId == idToMatch) { - // Found it! - return seed; - } - - hitLast = (last == itemCount - 1); - hitFirst = (first == 0); - - if (hitLast && hitFirst) { - // Looked at everything - break; - } - - if (hitFirst || (next && !hitLast)) { - // Either we hit the top, or we are trying to move down - last++; - seed = last; - - // Try going up next time - next = false; - } else if (hitLast || (!next && !hitFirst)) { - // Either we hit the bottom, or we are trying to move up - first--; - seed = first; - - // Try going down next time - next = true; - } - } - - return INVALID_POSITION; - } - - @TargetApi(16) - private View obtainView(int position, boolean[] isScrap) { - isScrap[0] = false; - - View scrapView = mRecycler.getTransientStateView(position); - if (scrapView != null) { - return scrapView; - } - - scrapView = mRecycler.getScrapView(position); - - final View child; - if (scrapView != null) { - child = mAdapter.getView(position, scrapView, this); - - if (child != scrapView) { - mRecycler.addScrapView(scrapView, position); - } else { - isScrap[0] = true; - } - } else { - child = mAdapter.getView(position, null, this); - } - - if (ViewCompat.getImportantForAccessibility(child) == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - ViewCompat.setImportantForAccessibility(child, - ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - if (mHasStableIds) { - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (lp == null) { - lp = generateDefaultLayoutParams(); - } else if (!checkLayoutParams(lp)) { - lp = generateLayoutParams(lp); - } - - lp.id = mAdapter.getItemId(position); - - child.setLayoutParams(lp); - } - - if (mAccessibilityDelegate == null) { - mAccessibilityDelegate = new ListItemAccessibilityDelegate(); - } - - ViewCompat.setAccessibilityDelegate(child, mAccessibilityDelegate); - - return child; - } - - void resetState() { - removeAllViewsInLayout(); - - mSelectedStart = 0; - mFirstPosition = 0; - mDataChanged = false; - mNeedSync = false; - mPendingSync = null; - mOldSelectedPosition = INVALID_POSITION; - mOldSelectedRowId = INVALID_ROW_ID; - - mOverScroll = 0; - - setSelectedPositionInt(INVALID_POSITION); - setNextSelectedPositionInt(INVALID_POSITION); - - mSelectorPosition = INVALID_POSITION; - mSelectorRect.setEmpty(); - - invalidate(); - } - - private void rememberSyncState() { - if (getChildCount() == 0) { - return; - } - - mNeedSync = true; - - if (mSelectedPosition >= 0) { - View child = getChildAt(mSelectedPosition - mFirstPosition); - - mSyncRowId = mNextSelectedRowId; - mSyncPosition = mNextSelectedPosition; - - if (child != null) { - mSpecificStart = (mIsVertical ? child.getTop() : child - .getLeft()); - } - - mSyncMode = SYNC_SELECTED_POSITION; - } else { - // Sync the based on the offset of the first view - View child = getChildAt(0); - ListAdapter adapter = getAdapter(); - - if (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) { - mSyncRowId = adapter.getItemId(mFirstPosition); - } else { - mSyncRowId = NO_ID; - } - - mSyncPosition = mFirstPosition; - - if (child != null) { - mSpecificStart = child.getTop(); - } - - mSyncMode = SYNC_FIRST_POSITION; - } - } - - private ContextMenuInfo createContextMenuInfo(View view, int position, - long id) { - return new AdapterContextMenuInfo(view, position, id); - } - - @TargetApi(11) - private void updateOnScreenCheckedViews() { - final int firstPos = mFirstPosition; - final int count = getChildCount(); - - final boolean useActivated = getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB; - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final int position = firstPos + i; - - if (child instanceof Checkable) { - ((Checkable) child).setChecked(mCheckStates.get(position)); - } else if (useActivated) { - child.setActivated(mCheckStates.get(position)); - } - } - } - - @Override - public boolean performItemClick(View view, int position, long id) { - boolean checkedStateChanged = false; - - if (mChoiceMode.compareTo(ChoiceMode.MULTIPLE) == 0) { - boolean checked = !mCheckStates.get(position, false); - mCheckStates.put(position, checked); - - if (mCheckedIdStates != null && mAdapter.hasStableIds()) { - if (checked) { - mCheckedIdStates - .put(mAdapter.getItemId(position), position); - } else { - mCheckedIdStates.delete(mAdapter.getItemId(position)); - } - } - - if (checked) { - mCheckedItemCount++; - } else { - mCheckedItemCount--; - } - - checkedStateChanged = true; - } else if (mChoiceMode.compareTo(ChoiceMode.SINGLE) == 0) { - boolean checked = !mCheckStates.get(position, false); - if (checked) { - mCheckStates.clear(); - mCheckStates.put(position, true); - - if (mCheckedIdStates != null && mAdapter.hasStableIds()) { - mCheckedIdStates.clear(); - mCheckedIdStates - .put(mAdapter.getItemId(position), position); - } - - mCheckedItemCount = 1; - } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { - mCheckedItemCount = 0; - } - - checkedStateChanged = true; - } - - if (checkedStateChanged) { - updateOnScreenCheckedViews(); - } - - return super.performItemClick(view, position, id); - } - - private boolean performLongPress(final View child, - final int longPressPosition, final long longPressId) { - // CHOICE_MODE_MULTIPLE_MODAL takes over long press. - boolean handled = false; - - OnItemLongClickListener listener = getOnItemLongClickListener(); - if (listener != null) { - handled = listener.onItemLongClick(TwoWayView.this, child, - longPressPosition, longPressId); - } - - if (!handled) { - mContextMenuInfo = createContextMenuInfo(child, longPressPosition, - longPressId); - handled = super.showContextMenuForChild(TwoWayView.this); - } - - if (handled) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } - - return handled; - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - if (mIsVertical) { - return new LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.WRAP_CONTENT); - } else { - return new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT); - } - } - - @Override - protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - return new LayoutParams(lp); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams lp) { - return lp instanceof LayoutParams; - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected ContextMenuInfo getContextMenuInfo() { - return mContextMenuInfo; - } - - @Override - public Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - SavedState ss = new SavedState(superState); - - if (mPendingSync != null) { - ss.selectedId = mPendingSync.selectedId; - ss.firstId = mPendingSync.firstId; - ss.viewStart = mPendingSync.viewStart; - ss.position = mPendingSync.position; - ss.height = mPendingSync.height; - - return ss; - } - - boolean haveChildren = (getChildCount() > 0 && mItemCount > 0); - long selectedId = getSelectedItemId(); - ss.selectedId = selectedId; - ss.height = getHeight(); - - if (selectedId >= 0) { - ss.viewStart = mSelectedStart; - ss.position = getSelectedItemPosition(); - ss.firstId = INVALID_POSITION; - } else if (haveChildren && mFirstPosition > 0) { - // Remember the position of the first child. - // We only do this if we are not currently at the top of - // the list, for two reasons: - // - // (1) The list may be in the process of becoming empty, in - // which case mItemCount may not be 0, but if we try to - // ask for any information about position 0 we will crash. - // - // (2) Being "at the top" seems like a special case, anyway, - // and the user wouldn't expect to end up somewhere else when - // they revisit the list even if its content has changed. - - View child = getChildAt(0); - ss.viewStart = (mIsVertical ? child.getTop() : child.getLeft()); - - int firstPos = mFirstPosition; - if (firstPos >= mItemCount) { - firstPos = mItemCount - 1; - } - - ss.position = firstPos; - ss.firstId = mAdapter.getItemId(firstPos); - } else { - ss.viewStart = 0; - ss.firstId = INVALID_POSITION; - ss.position = 0; - } - - if (mCheckStates != null) { - ss.checkState = cloneCheckStates(); - } - - if (mCheckedIdStates != null) { - final LongSparseArray idState = new LongSparseArray(); - - final int count = mCheckedIdStates.size(); - for (int i = 0; i < count; i++) { - idState.put(mCheckedIdStates.keyAt(i), - mCheckedIdStates.valueAt(i)); - } - - ss.checkIdState = idState; - } - - ss.checkedItemCount = mCheckedItemCount; - - return ss; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - - mDataChanged = true; - mSyncHeight = ss.height; - - if (ss.selectedId >= 0) { - mNeedSync = true; - mPendingSync = ss; - mSyncRowId = ss.selectedId; - mSyncPosition = ss.position; - mSpecificStart = ss.viewStart; - mSyncMode = SYNC_SELECTED_POSITION; - } else if (ss.firstId >= 0) { - setSelectedPositionInt(INVALID_POSITION); - - // Do this before setting mNeedSync since setNextSelectedPosition - // looks at mNeedSync - setNextSelectedPositionInt(INVALID_POSITION); - - mSelectorPosition = INVALID_POSITION; - mNeedSync = true; - mPendingSync = ss; - mSyncRowId = ss.firstId; - mSyncPosition = ss.position; - mSpecificStart = ss.viewStart; - mSyncMode = SYNC_FIRST_POSITION; - } - - if (ss.checkState != null) { - mCheckStates = ss.checkState; - } - - if (ss.checkIdState != null) { - mCheckedIdStates = ss.checkIdState; - } - - mCheckedItemCount = ss.checkedItemCount; - - requestLayout(); - } - - public static class LayoutParams extends ViewGroup.LayoutParams { - /** - * Type of this view as reported by the adapter - */ - int viewType; - - /** - * The stable ID of the item this view displays - */ - long id = -1; - - /** - * The position the view was removed from when pulled out of the scrap - * heap. - * - * @hide - */ - int scrappedFromPosition; - - /** - * When a TwoWayView is measured with an AT_MOST measure spec, it needs - * to obtain children views to measure itself. When doing so, the - * children are not attached to the window, but put in the recycler - * which assumes they've been attached before. Setting this flag will - * force the reused view to be attached to the window rather than just - * attached to the parent. - */ - boolean forceAdd; - - public LayoutParams(int width, int height) { - super(width, height); - - if (this.width == MATCH_PARENT) { - Log.w(LOGTAG, - "Constructing LayoutParams with width FILL_PARENT " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.width = WRAP_CONTENT; - } - - if (this.height == MATCH_PARENT) { - Log.w(LOGTAG, - "Constructing LayoutParams with height FILL_PARENT " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.height = WRAP_CONTENT; - } - } - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - - if (this.width == MATCH_PARENT) { - Log.w(LOGTAG, - "Inflation setting LayoutParams width to MATCH_PARENT - " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.width = MATCH_PARENT; - } - - if (this.height == MATCH_PARENT) { - Log.w(LOGTAG, - "Inflation setting LayoutParams height to MATCH_PARENT - " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.height = WRAP_CONTENT; - } - } - - public LayoutParams(ViewGroup.LayoutParams other) { - super(other); - - if (this.width == MATCH_PARENT) { - Log.w(LOGTAG, - "Constructing LayoutParams with height MATCH_PARENT - " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.width = WRAP_CONTENT; - } - - if (this.height == MATCH_PARENT) { - Log.w(LOGTAG, - "Constructing LayoutParams with height MATCH_PARENT - " - + "does not make much sense as the view might change orientation. " - + "Falling back to WRAP_CONTENT"); - this.height = WRAP_CONTENT; - } - } - } - - class RecycleBin { - private RecyclerListener mRecyclerListener; - private int mFirstActivePosition; - private View[] mActiveViews = new View[0]; - private ArrayList[] mScrapViews; - private int mViewTypeCount; - private ArrayList mCurrentScrap; - private SparseArrayCompat mTransientStateViews; - - public void setViewTypeCount(int viewTypeCount) { - if (viewTypeCount < 1) { - throw new IllegalArgumentException( - "Can't have a viewTypeCount < 1"); - } - - @SuppressWarnings("unchecked") - ArrayList[] scrapViews = new ArrayList[viewTypeCount]; - for (int i = 0; i < viewTypeCount; i++) { - scrapViews[i] = new ArrayList(); - } - - mViewTypeCount = viewTypeCount; - mCurrentScrap = scrapViews[0]; - mScrapViews = scrapViews; - } - - public void markChildrenDirty() { - if (mViewTypeCount == 1) { - final ArrayList scrap = mCurrentScrap; - final int scrapCount = scrap.size(); - - for (int i = 0; i < scrapCount; i++) { - scrap.get(i).forceLayout(); - } - } else { - final int typeCount = mViewTypeCount; - for (int i = 0; i < typeCount; i++) { - final ArrayList scrap = mScrapViews[i]; - final int scrapCount = scrap.size(); - - for (int j = 0; j < scrapCount; j++) { - scrap.get(j).forceLayout(); - } - } - } - - if (mTransientStateViews != null) { - final int count = mTransientStateViews.size(); - for (int i = 0; i < count; i++) { - mTransientStateViews.valueAt(i).forceLayout(); - } - } - } - - public boolean shouldRecycleViewType(int viewType) { - return viewType >= 0; - } - - void clear() { - if (mViewTypeCount == 1) { - final ArrayList scrap = mCurrentScrap; - final int scrapCount = scrap.size(); - - for (int i = 0; i < scrapCount; i++) { - removeDetachedView(scrap.remove(scrapCount - 1 - i), false); - } - } else { - final int typeCount = mViewTypeCount; - for (int i = 0; i < typeCount; i++) { - final ArrayList scrap = mScrapViews[i]; - final int scrapCount = scrap.size(); - - for (int j = 0; j < scrapCount; j++) { - removeDetachedView(scrap.remove(scrapCount - 1 - j), - false); - } - } - } - - if (mTransientStateViews != null) { - mTransientStateViews.clear(); - } - } - - void fillActiveViews(int childCount, int firstActivePosition) { - if (mActiveViews.length < childCount) { - mActiveViews = new View[childCount]; - } - - mFirstActivePosition = firstActivePosition; - - final View[] activeViews = mActiveViews; - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - - // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active - // views. - // However, we will NOT place them into scrap views. - activeViews[i] = child; - } - } - - View getActiveView(int position) { - final int index = position - mFirstActivePosition; - final View[] activeViews = mActiveViews; - - if (index >= 0 && index < activeViews.length) { - final View match = activeViews[index]; - activeViews[index] = null; - - return match; - } - - return null; - } - - View getTransientStateView(int position) { - if (mTransientStateViews == null) { - return null; - } - - final int index = mTransientStateViews.indexOfKey(position); - if (index < 0) { - return null; - } - - final View result = mTransientStateViews.valueAt(index); - mTransientStateViews.removeAt(index); - - return result; - } - - void clearTransientStateViews() { - if (mTransientStateViews != null) { - mTransientStateViews.clear(); - } - } - - View getScrapView(int position) { - if (mViewTypeCount == 1) { - return retrieveFromScrap(mCurrentScrap, position); - } else { - int whichScrap = mAdapter.getItemViewType(position); - if (whichScrap >= 0 && whichScrap < mScrapViews.length) { - return retrieveFromScrap(mScrapViews[whichScrap], position); - } - } - - return null; - } - - @TargetApi(14) - void addScrapView(View scrap, int position) { - LayoutParams lp = (LayoutParams) scrap.getLayoutParams(); - if (lp == null) { - return; - } - - lp.scrappedFromPosition = position; - - final int viewType = lp.viewType; - final boolean scrapHasTransientState = ViewCompat - .hasTransientState(scrap); - - // Don't put views that should be ignored into the scrap heap - if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { - if (scrapHasTransientState) { - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArrayCompat(); - } - - mTransientStateViews.put(position, scrap); - } - - return; - } - - if (mViewTypeCount == 1) { - mCurrentScrap.add(scrap); - } else { - mScrapViews[viewType].add(scrap); - } - - // FIXME: Unfortunately, ViewCompat.setAccessibilityDelegate() - // doesn't accept - // null delegates. - if (Build.VERSION.SDK_INT >= 14) { - scrap.setAccessibilityDelegate(null); - } - - if (mRecyclerListener != null) { - mRecyclerListener.onMovedToScrapHeap(scrap); - } - } - - @TargetApi(14) - void scrapActiveViews() { - final View[] activeViews = mActiveViews; - final boolean multipleScraps = (mViewTypeCount > 1); - - ArrayList scrapViews = mCurrentScrap; - final int count = activeViews.length; - - for (int i = count - 1; i >= 0; i--) { - final View victim = activeViews[i]; - if (victim != null) { - final LayoutParams lp = (LayoutParams) victim - .getLayoutParams(); - int whichScrap = lp.viewType; - - activeViews[i] = null; - - final boolean scrapHasTransientState = ViewCompat - .hasTransientState(victim); - if (!shouldRecycleViewType(whichScrap) - || scrapHasTransientState) { - if (scrapHasTransientState) { - removeDetachedView(victim, false); - - if (mTransientStateViews == null) { - mTransientStateViews = new SparseArrayCompat(); - } - - mTransientStateViews.put(mFirstActivePosition + i, - victim); - } - - continue; - } - - if (multipleScraps) { - scrapViews = mScrapViews[whichScrap]; - } - - lp.scrappedFromPosition = mFirstActivePosition + i; - scrapViews.add(victim); - - // FIXME: Unfortunately, - // ViewCompat.setAccessibilityDelegate() doesn't accept - // null delegates. - if (Build.VERSION.SDK_INT >= 14) { - victim.setAccessibilityDelegate(null); - } - - if (mRecyclerListener != null) { - mRecyclerListener.onMovedToScrapHeap(victim); - } - } - } - - pruneScrapViews(); - } - - private void pruneScrapViews() { - final int maxViews = mActiveViews.length; - final int viewTypeCount = mViewTypeCount; - final ArrayList[] scrapViews = mScrapViews; - - for (int i = 0; i < viewTypeCount; ++i) { - final ArrayList scrapPile = scrapViews[i]; - int size = scrapPile.size(); - final int extras = size - maxViews; - - size--; - - for (int j = 0; j < extras; j++) { - removeDetachedView(scrapPile.remove(size--), false); - } - } - - if (mTransientStateViews != null) { - for (int i = 0; i < mTransientStateViews.size(); i++) { - final View v = mTransientStateViews.valueAt(i); - if (!ViewCompat.hasTransientState(v)) { - mTransientStateViews.removeAt(i); - i--; - } - } - } - } - - void reclaimScrapViews(List views) { - if (mViewTypeCount == 1) { - views.addAll(mCurrentScrap); - } else { - final int viewTypeCount = mViewTypeCount; - final ArrayList[] scrapViews = mScrapViews; - - for (int i = 0; i < viewTypeCount; ++i) { - final ArrayList scrapPile = scrapViews[i]; - views.addAll(scrapPile); - } - } - } - - View retrieveFromScrap(ArrayList scrapViews, int position) { - int size = scrapViews.size(); - if (size <= 0) { - return null; - } - - for (int i = 0; i < size; i++) { - final View scrapView = scrapViews.get(i); - final LayoutParams lp = (LayoutParams) scrapView - .getLayoutParams(); - - if (lp.scrappedFromPosition == position) { - scrapViews.remove(i); - return scrapView; - } - } - - return scrapViews.remove(size - 1); - } - } - - @Override - public void setEmptyView(View emptyView) { - super.setEmptyView(emptyView); - mEmptyView = emptyView; - updateEmptyStatus(); - } - - @Override - public void setFocusable(boolean focusable) { - final ListAdapter adapter = getAdapter(); - final boolean empty = (adapter == null || adapter.getCount() == 0); - - mDesiredFocusableState = focusable; - if (!focusable) { - mDesiredFocusableInTouchModeState = false; - } - - super.setFocusable(focusable && !empty); - } - - @Override - public void setFocusableInTouchMode(boolean focusable) { - final ListAdapter adapter = getAdapter(); - final boolean empty = (adapter == null || adapter.getCount() == 0); - - mDesiredFocusableInTouchModeState = focusable; - if (focusable) { - mDesiredFocusableState = true; - } - - super.setFocusableInTouchMode(focusable && !empty); - } - - private void checkFocus() { - final ListAdapter adapter = getAdapter(); - final boolean focusable = (adapter != null && adapter.getCount() > 0); - - // The order in which we set focusable in touch mode/focusable may - // matter - // for the client, see View.setFocusableInTouchMode() comments for more - // details - super.setFocusableInTouchMode(focusable - && mDesiredFocusableInTouchModeState); - super.setFocusable(focusable && mDesiredFocusableState); - - if (mEmptyView != null) { - updateEmptyStatus(); - } - } - - @SuppressLint("WrongCall") - private void updateEmptyStatus() { - final boolean isEmpty = (mAdapter == null || mAdapter.isEmpty()); - - if (isEmpty) { - if (mEmptyView != null) { - mEmptyView.setVisibility(View.VISIBLE); - setVisibility(View.GONE); - } else { - // If the caller just removed our empty view, make sure the list - // view is visible - setVisibility(View.VISIBLE); - } - - // We are now GONE, so pending layouts will not be dispatched. - // Force one here to make sure that the state of the list matches - // the state of the adapter. - if (mDataChanged) { - onLayout(false, getLeft(), getTop(), getRight(), getBottom()); - } - } else { - if (mEmptyView != null) { - mEmptyView.setVisibility(View.GONE); - } - - setVisibility(View.VISIBLE); - } - } - - private class AdapterDataSetObserver extends DataSetObserver { - private Parcelable mInstanceState = null; - - @Override - public void onChanged() { - mDataChanged = true; - mOldItemCount = mItemCount; - mItemCount = getAdapter().getCount(); - - // Detect the case where a cursor that was previously invalidated - // has - // been re-populated with new data. - if (TwoWayView.this.mHasStableIds && mInstanceState != null - && mOldItemCount == 0 && mItemCount > 0) { - TwoWayView.this.onRestoreInstanceState(mInstanceState); - mInstanceState = null; - } else { - rememberSyncState(); - } - - checkFocus(); - requestLayout(); - } - - @Override - public void onInvalidated() { - mDataChanged = true; - - if (TwoWayView.this.mHasStableIds) { - // Remember the current state for the case where our hosting - // activity is being - // stopped and later restarted - mInstanceState = TwoWayView.this.onSaveInstanceState(); - } - - // Data is invalid so we should reset our state - mOldItemCount = mItemCount; - mItemCount = 0; - - mSelectedPosition = INVALID_POSITION; - mSelectedRowId = INVALID_ROW_ID; - - mNextSelectedPosition = INVALID_POSITION; - mNextSelectedRowId = INVALID_ROW_ID; - - mNeedSync = false; - - checkFocus(); - requestLayout(); - } - } - - static class SavedState extends BaseSavedState { - long selectedId; - long firstId; - int viewStart; - int position; - int height; - int checkedItemCount; - SparseBooleanArray checkState; - LongSparseArray checkIdState; - - /** - * Constructor called from {@link TwoWayView#onSaveInstanceState()} - */ - SavedState(Parcelable superState) { - super(superState); - } - - /** - * Constructor called from {@link #CREATOR} - */ - private SavedState(Parcel in) { - super(in); - - selectedId = in.readLong(); - firstId = in.readLong(); - viewStart = in.readInt(); - position = in.readInt(); - height = in.readInt(); - - checkedItemCount = in.readInt(); - checkState = in.readSparseBooleanArray(); - - final int N = in.readInt(); - if (N > 0) { - checkIdState = new LongSparseArray(); - for (int i = 0; i < N; i++) { - final long key = in.readLong(); - final int value = in.readInt(); - checkIdState.put(key, value); - } - } - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - - out.writeLong(selectedId); - out.writeLong(firstId); - out.writeInt(viewStart); - out.writeInt(position); - out.writeInt(height); - - out.writeInt(checkedItemCount); - out.writeSparseBooleanArray(checkState); - - final int N = checkIdState != null ? checkIdState.size() : 0; - out.writeInt(N); - - for (int i = 0; i < N; i++) { - out.writeLong(checkIdState.keyAt(i)); - out.writeInt(checkIdState.valueAt(i)); - } - } - - @Override - public String toString() { - return "TwoWayView.SavedState{" - + Integer.toHexString(System.identityHashCode(this)) - + " selectedId=" + selectedId + " firstId=" + firstId - + " viewStart=" + viewStart + " height=" + height - + " position=" + position + " checkState=" + checkState - + "}"; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - private class SelectionNotifier implements Runnable { - @Override - public void run() { - if (mDataChanged) { - // Data has changed between when this SelectionNotifier - // was posted and now. We need to wait until the AdapterView - // has been synched to the new data. - if (mAdapter != null) { - post(this); - } - } else { - fireOnSelected(); - performAccessibilityActionsOnSelected(); - } - } - } - - private class WindowRunnnable { - private int mOriginalAttachCount; - - public void rememberWindowAttachCount() { - mOriginalAttachCount = getWindowAttachCount(); - } - - public boolean sameWindow() { - return hasWindowFocus() - && getWindowAttachCount() == mOriginalAttachCount; - } - } - - private class PerformClick extends WindowRunnnable implements Runnable { - int mClickMotionPosition; - - @Override - public void run() { - if (mDataChanged) { - return; - } - - final ListAdapter adapter = mAdapter; - final int motionPosition = mClickMotionPosition; - - if (adapter != null && mItemCount > 0 - && motionPosition != INVALID_POSITION - && motionPosition < adapter.getCount() && sameWindow()) { - - final View child = getChildAt(motionPosition - mFirstPosition); - if (child != null) { - performItemClick(child, motionPosition, - adapter.getItemId(motionPosition)); - } - } - } - } - - private final class CheckForTap implements Runnable { - @Override - public void run() { - if (mTouchMode != TOUCH_MODE_DOWN) { - return; - } - - mTouchMode = TOUCH_MODE_TAP; - - final View child = getChildAt(mMotionPosition - mFirstPosition); - if (child != null && !child.hasFocusable()) { - mLayoutMode = LAYOUT_NORMAL; - - if (!mDataChanged) { - setPressed(true); - child.setPressed(true); - - layoutChildren(); - positionSelector(mMotionPosition, child); - refreshDrawableState(); - - positionSelector(mMotionPosition, child); - refreshDrawableState(); - - final boolean longClickable = isLongClickable(); - - if (mSelector != null) { - Drawable d = mSelector.getCurrent(); - - if (d != null && d instanceof TransitionDrawable) { - if (longClickable) { - final int longPressTimeout = ViewConfiguration - .getLongPressTimeout(); - ((TransitionDrawable) d) - .startTransition(longPressTimeout); - } else { - ((TransitionDrawable) d).resetTransition(); - } - } - } - - if (longClickable) { - triggerCheckForLongPress(); - } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; - } - } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; - } - } - } - } - - private class CheckForLongPress extends WindowRunnnable implements Runnable { - @Override - public void run() { - final int motionPosition = mMotionPosition; - final View child = getChildAt(motionPosition - mFirstPosition); - - if (child != null) { - final long longPressId = mAdapter.getItemId(mMotionPosition); - - boolean handled = false; - if (sameWindow() && !mDataChanged) { - handled = performLongPress(child, motionPosition, - longPressId); - } - - if (handled) { - mTouchMode = TOUCH_MODE_REST; - setPressed(false); - child.setPressed(false); - } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; - } - } - } - } - - private class CheckForKeyLongPress extends WindowRunnnable implements - Runnable { - public void run() { - if (!isPressed() || mSelectedPosition < 0) { - return; - } - - final int index = mSelectedPosition - mFirstPosition; - final View v = getChildAt(index); - - if (!mDataChanged) { - boolean handled = false; - - if (sameWindow()) { - handled = performLongPress(v, mSelectedPosition, - mSelectedRowId); - } - - if (handled) { - setPressed(false); - v.setPressed(false); - } - } else { - setPressed(false); - - if (v != null) { - v.setPressed(false); - } - } - } - } - - private static class ArrowScrollFocusResult { - private int mSelectedPosition; - private int mAmountToScroll; - - /** - * How {@link TwoWayView#arrowScrollFocused} returns its values. - */ - void populate(int selectedPosition, int amountToScroll) { - mSelectedPosition = selectedPosition; - mAmountToScroll = amountToScroll; - } - - public int getSelectedPosition() { - return mSelectedPosition; - } - - public int getAmountToScroll() { - return mAmountToScroll; - } - } - - private class ListItemAccessibilityDelegate extends - AccessibilityDelegateCompat { - @Override - public void onInitializeAccessibilityNodeInfo(View host, - AccessibilityNodeInfoCompat info) { - super.onInitializeAccessibilityNodeInfo(host, info); - - final int position = getPositionForView(host); - final ListAdapter adapter = getAdapter(); - - // Cannot perform actions on invalid items - if (position == INVALID_POSITION || adapter == null) { - return; - } - - // Cannot perform actions on disabled items - if (!isEnabled() || !adapter.isEnabled(position)) { - return; - } - - if (position == getSelectedItemPosition()) { - info.setSelected(true); - info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION); - } else { - info.addAction(AccessibilityNodeInfoCompat.ACTION_SELECT); - } - - if (isClickable()) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); - info.setClickable(true); - } - - if (isLongClickable()) { - info.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK); - info.setLongClickable(true); - } - } - - @Override - public boolean performAccessibilityAction(View host, int action, - Bundle arguments) { - if (super.performAccessibilityAction(host, action, arguments)) { - return true; - } - - final int position = getPositionForView(host); - final ListAdapter adapter = getAdapter(); - - // Cannot perform actions on invalid items - if (position == INVALID_POSITION || adapter == null) { - return false; - } - - // Cannot perform actions on disabled items - if (!isEnabled() || !adapter.isEnabled(position)) { - return false; - } - - final long id = getItemIdAtPosition(position); - - switch (action) { - case AccessibilityNodeInfoCompat.ACTION_CLEAR_SELECTION: - if (getSelectedItemPosition() == position) { - setSelection(INVALID_POSITION); - return true; - } - return false; - - case AccessibilityNodeInfoCompat.ACTION_SELECT: - if (getSelectedItemPosition() != position) { - setSelection(position); - return true; - } - return false; - - case AccessibilityNodeInfoCompat.ACTION_CLICK: - if (isClickable()) { - return performItemClick(host, position, id); - } - return false; - - case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK: - if (isLongClickable()) { - return performLongPress(host, position, id); - } - return false; - } - - return false; - } - } -} \ No newline at end of file diff --git a/android/src/com/artifex/mupdflib/WidgetType.java b/android/src/com/artifex/mupdflib/WidgetType.java new file mode 100644 index 0000000..80edef9 --- /dev/null +++ b/android/src/com/artifex/mupdflib/WidgetType.java @@ -0,0 +1,9 @@ +package com.artifex.mupdflib; + +public enum WidgetType { + NONE, + TEXT, + LISTBOX, + COMBOBOX, + SIGNATURE +} diff --git a/android/src/com/mykingdom/mupdf/MupdfModule.java b/android/src/com/mykingdom/mupdf/MupdfModule.java index 0f37393..d2ed322 100644 --- a/android/src/com/mykingdom/mupdf/MupdfModule.java +++ b/android/src/com/mykingdom/mupdf/MupdfModule.java @@ -20,9 +20,6 @@ public class MupdfModule extends KrollModule { // Standard Debugging variables private static final String TAG = "MupdfModule"; - @Kroll.constant public static final int ERROR_TEXT_NOT_FOUND = 404; - @Kroll.constant public static final int ERROR_NO_FURTHER_OCCURRENCES_FOUND = 402; - // You can define constants with @Kroll.constant, for example: // @Kroll.constant public static final String EXTERNAL_NAME = value; diff --git a/android/src/com/mykingdom/mupdf/PDFReaderProxy.java b/android/src/com/mykingdom/mupdf/ViewProxy.java similarity index 89% rename from android/src/com/mykingdom/mupdf/PDFReaderProxy.java rename to android/src/com/mykingdom/mupdf/ViewProxy.java index 5fc8935..34bb1e7 100644 --- a/android/src/com/mykingdom/mupdf/PDFReaderProxy.java +++ b/android/src/com/mykingdom/mupdf/ViewProxy.java @@ -29,12 +29,11 @@ import com.artifex.mupdflib.MuPDFCore; import com.artifex.mupdflib.MuPDFPageAdapter; import com.artifex.mupdflib.MuPDFReaderView; -import com.artifex.mupdflib.PDFPreviewGridActivityData; import com.artifex.mupdflib.PageView; import com.artifex.mupdflib.ReaderView; +import com.artifex.mupdflib.SearchTask; import com.artifex.mupdflib.SearchTaskResult; import com.artifex.mupdflib.FilePicker.FilePickerSupport; -import com.artifex.mupdflib.SearchTask; import android.app.Activity; import android.net.Uri; @@ -46,7 +45,7 @@ // This proxy can be created by calling Mupdf.createPDFReader({file: "path"}) @Kroll.proxy(creatableInModule = MupdfModule.class) -public class PDFReaderProxy extends TiViewProxy { +public class ViewProxy extends TiViewProxy { // Standard Debugging variables private static final String LCAT = "PDFReaderProxy"; @@ -65,6 +64,7 @@ private class PDFReaderView extends TiUIView implements FilePickerSupport { private TiApplication tiApplication; private int currentPage; private int pageCount; + private boolean renderResult = false; public PDFReaderView(TiViewProxy proxy) { @@ -157,15 +157,19 @@ private MuPDFCore openFile(String path) { @Override protected void onTextFound(SearchTaskResult result) { - SearchTaskResult.set(result); - mDocView.setDisplayedViewIndex(result.pageNumber); + if (renderResult) { + SearchTaskResult.set(result); + mDocView.setDisplayedViewIndex(result.pageNumber); + } else { + SearchTaskResult.set(null); + } mDocView.resetupChildren(); if (mSearchCallback != null) { HashMap params = new HashMap(); params.put(TiC.PROPERTY_COUNT, result.searchBoxes.length); params.put(TiC.PROPERTY_CURRENT_PAGE, - result.pageNumber); + result.pageNumber + 1); params.put(TiC.EVENT_PROPERTY_ERROR, false); params.put(TiC.PROPERTY_SUCCESS, true); mSearchCallback.call(getKrollObject(), params); @@ -173,22 +177,18 @@ protected void onTextFound(SearchTaskResult result) { } @Override - protected void onTextNotFound(int code) { + protected void onTextNotFound(int pageNumber) { if (mSearchCallback != null) { HashMap params = new HashMap(); params.put(TiC.PROPERTY_COUNT, 0); - params.put(TiC.PROPERTY_CURRENT_PAGE, 0); + params.put(TiC.PROPERTY_CURRENT_PAGE, + pageNumber + 1); params.put(TiC.EVENT_PROPERTY_ERROR, true); - params.put(TiC.ERROR_PROPERTY_ERRORCODE, code); params.put(TiC.PROPERTY_SUCCESS, false); mSearchCallback.call(getKrollObject(), params); } } - }; - // New file: drop the old outline data - // OutlineActivityData.set(null); - PDFPreviewGridActivityData.set(null); } catch (Exception e) { System.out.println(e); return null; @@ -225,13 +225,13 @@ public void onSearch(KrollFunction callback) { mSearchCallback = callback; } - public void search(String key, int direction) { + public void search(String key, int pageNumber, boolean showResult) { if (mSearchTask == null) return; - int displayPage = mDocView.getDisplayedViewIndex(); - SearchTaskResult r = SearchTaskResult.get(); - int searchPage = r != null ? r.pageNumber : -1; - mSearchTask.go(key, direction, displayPage, searchPage); + // stop any on going search + mSearchTask.stop(); + renderResult = showResult; + mSearchTask.go(key, 0, currentPage - 1, pageNumber - 1); } public void setHighlightColor(String color) { @@ -246,7 +246,7 @@ public void performPickFor(FilePicker picker) { } // Constructor - public PDFReaderProxy() { + public ViewProxy() { super(); } @@ -264,10 +264,7 @@ public PDFReaderView getPDFReaderView() { @Override public void releaseViews() { - // Release PDF Viewer Memory - Log.d("MUPDF", "RELEASING VIEW"); getPDFReaderView().cleanup(); - super.releaseViews(); } @@ -332,8 +329,13 @@ public void onSearch(KrollFunction callback) { } @Kroll.method - public void search(String key, int direction) { - getPDFReaderView().search(key, direction); + public void search(String key, int pageNumber, + @Kroll.argument(optional = true) Object showResult) { + boolean enable = false; + if (showResult != null) { + enable = TiConvert.toBoolean(showResult); + } + getPDFReaderView().search(key, pageNumber, enable); } @Kroll.method diff --git a/example/app.js b/example/app.js index 9cd7c3f..5a9cab2 100644 --- a/example/app.js +++ b/example/app.js @@ -18,7 +18,14 @@ if (!file.exists()) { console.log(">>EXISTS>>>" + file.exists()); -var pdfReader = READER_MODULE.createPDFReader({ +var processDialog = Ti.UI.Android.createProgressIndicator({ + message : 'Searching...', + location : Ti.UI.Android.PROGRESS_INDICATOR_DIALOG, + type : Ti.UI.Android.PROGRESS_INDICATOR_INDETERMINANT, + cancelable : false +}); + +var pdfReader = READER_MODULE.createView({ file : file }); @@ -60,13 +67,17 @@ win.addEventListener("open", function(e) { }); searchItem.addEventListener("click", function(e) { var toast = Ti.UI.createNotification({ - message : "Search for the keyword 'for' in the entire pdf", + message : "Search for the total occurences of keyword 'for' in the entire pdf. Note : Touch events will be disabled during search", duration : Ti.UI.NOTIFICATION_DURATION_LONG }); toast.show(); count = 0; + processDialog.show(); + pdfReader.touchEnabled = false; pdfReader.onSearch(searchResult); - pdfReader.search("for", 0); + //start search from page no. 1. + //third parameter is optional, defaults to false. Disable the rendering of the search. If true the page will be rendered with results highlighted + pdfReader.search("for", 1, false); }); var previousItem = e.menu.add({ title : "Previous", @@ -88,7 +99,7 @@ win.addEventListener("open", function(e) { }); searchPreviousItem.addEventListener("click", function(e) { pdfReader.onSearch(logSearch); - pdfReader.search("for", -1); + pdfReader.search("for", pdfReader.getCurrentPage() - 1, true); }); var searchNextItem = e.menu.add({ title : "Search Next", @@ -96,45 +107,44 @@ win.addEventListener("open", function(e) { }); searchNextItem.addEventListener("click", function(e) { pdfReader.onSearch(logSearch); - pdfReader.search("for", 1); + pdfReader.search("for", pdfReader.getCurrentPage() + 1, true); }); var toggleHightLight = e.menu.add({ title : "Toggle highlight", showAsAction : Ti.Android.SHOW_AS_ACTION_IF_ROOM }); toggleHightLight.addEventListener("click", function(e) { - enabled = !enabled; - pdfReader.setHighlightColor( enabled ? "#0000FF" : "transparent"); + pdfReader.setHighlightColor( enabled ? "#500000FF" : "transparent"); pdfReader.onSearch(logSearch); - pdfReader.search("for", 0); + //search and render results for the current page + pdfReader.search("for", pdfReader.getCurrentPage(), true); + enabled = !enabled; }); }; activity.invalidateOptionsMenu(); }); +function logSearch(evt) { + console.log(evt); +} + function searchResult(result) { console.log(result); - if (count == 0 && result.error) { - if (result.code == READER_MODULE.ERROR_TEXT_NOT_FOUND) { - alert("No matches found"); - } else if (result.code == READER_MODULE.ERROR_NO_FURTHER_OCCURRENCES_FOUND) { - alert("No more occurrences on the given direction"); - } - return; - } count += result.count; if (result.success && result.currentPage < pdfReader.getPageCount()) { - pdfReader.search("for", 1); + // search for next page until end of the pdf + pdfReader.search("for", result.currentPage + 1); } else { - pdfReader.setCurrentPage(1); - alert("Total occurence : " + count); + processDialog.hide(); + pdfReader.touchEnabled = true; + if (count == 0) { + alert("No matches found"); + } else { + alert("Total occurence : " + count); + } } } -function logSearch(evt) { - console.log(evt); -} - Ti.Gesture.addEventListener("orientationchange", function() { pdfReader.setCurrentPage(pdfReader.getCurrentPage()); });