зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
bee8ae28a0
|
@ -19,13 +19,13 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
|
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
|
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0e31f35a2a77301e91baa8a237aa9e9fa4076084"/>
|
||||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="973367035a1f2545f3dad6e40e354463dc56a7f4"/>
|
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="346c7694b156a3933f3d87cbc077c657e2ce571f"/>
|
||||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="832f4acaf481a19031e479a40b03d9ce5370ddee"/>
|
||||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d0aa65b140a45016975ed0ecf35f280dd336e1d3"/>
|
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d0aa65b140a45016975ed0ecf35f280dd336e1d3"/>
|
||||||
|
|
|
@ -15,15 +15,15 @@
|
||||||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||||
|
|
|
@ -19,13 +19,13 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
|
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
</project>
|
</project>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
"remote": "",
|
"remote": "",
|
||||||
"branch": ""
|
"branch": ""
|
||||||
},
|
},
|
||||||
"revision": "387f4c0123a7e82eae4c83f88f73e81f907c00e2",
|
"revision": "aba00cfd579caaf205e05c269f0a8100f242f39c",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bd5065ced020014df5fd45259fba1ac32d65673b"/>
|
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="41cc1de26e4edbe12add0009cdc0bd292f2e94fe"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="44b04243e31cd16f3baf54fcd4b5fecf9deb8b94"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="c510babaf88dfa2cfe2c202afb2649ee124569af"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8ba14125aba912707f44761f194339e6e59701b7"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="23db533981ee2cd04fc5d946420402aed2792381"/>
|
||||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||||
<!-- Stock Android things -->
|
<!-- Stock Android things -->
|
||||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1402612020000">
|
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1403216209000">
|
||||||
<emItems>
|
<emItems>
|
||||||
<emItem blockID="i454" id="sqlmoz@facebook.com">
|
<emItem blockID="i454" id="sqlmoz@facebook.com">
|
||||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||||
|
@ -88,6 +88,12 @@
|
||||||
</versionRange>
|
</versionRange>
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
|
</emItem>
|
||||||
|
<emItem blockID="i626" id="{20AD702C-661E-4534-8CE9-BA4EC9AD6ECC}">
|
||||||
|
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||||
|
</versionRange>
|
||||||
|
<prefs>
|
||||||
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
|
<emItem blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
|
||||||
<versionRange minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
|
<versionRange minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
|
||||||
|
@ -304,10 +310,8 @@
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i496" id="{ACAA314B-EEBA-48e4-AD47-84E31C44796C}">
|
<emItem blockID="i47" id="youtube@youtube2.com">
|
||||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
<prefs>
|
||||||
</versionRange>
|
|
||||||
<prefs>
|
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i360" id="ytd@mybrowserbar.com">
|
<emItem blockID="i360" id="ytd@mybrowserbar.com">
|
||||||
|
@ -365,8 +369,8 @@
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i584" id="{52b0f3db-f988-4788-b9dc-861d016f4487}">
|
<emItem blockID="i624" id="/^({b95faac1-a3d7-4d69-8943-ddd5a487d966}|{ecce0073-a837-45a2-95b9-600420505f7e}|{2713b394-286f-4d7c-89ea-4174eeab9f5a}|{da7a20cf-bef4-4342-ad78-0240fdf87055})$/">
|
||||||
<versionRange minVersion="0" maxVersion="0.1.9999999" severity="1">
|
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||||
</versionRange>
|
</versionRange>
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
|
@ -697,7 +701,7 @@
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i620" id="{21EAF666-26B3-4A3C-ABD0-CA2F5A326744}">
|
<emItem blockID="i620" id="{21EAF666-26B3-4A3C-ABD0-CA2F5A326744}">
|
||||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||||
</versionRange>
|
</versionRange>
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
|
@ -713,6 +717,12 @@
|
||||||
</versionRange>
|
</versionRange>
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
|
</emItem>
|
||||||
|
<emItem blockID="i584" id="{52b0f3db-f988-4788-b9dc-861d016f4487}">
|
||||||
|
<versionRange minVersion="0" maxVersion="0.1.9999999" severity="1">
|
||||||
|
</versionRange>
|
||||||
|
<prefs>
|
||||||
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i370" id="happylyrics@hpyproductions.net">
|
<emItem blockID="i370" id="happylyrics@hpyproductions.net">
|
||||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||||
|
@ -1211,8 +1221,10 @@
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i47" id="youtube@youtube2.com">
|
<emItem blockID="i622" id="/^({ebd898f8-fcf6-4694-bc3b-eabc7271eeb1}|{46008e0d-47ac-4daa-a02a-5eb69044431a}|{213c8ed6-1d78-4d8f-8729-25006aa86a76}|{fa23121f-ee7c-4bd8-8c06-123d087282c5}|{19803860-b306-423c-bbb5-f60a7d82cde5})$/">
|
||||||
<prefs>
|
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||||
|
</versionRange>
|
||||||
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
|
<emItem blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
|
||||||
|
@ -1507,6 +1519,12 @@
|
||||||
</versionRange>
|
</versionRange>
|
||||||
<prefs>
|
<prefs>
|
||||||
</prefs>
|
</prefs>
|
||||||
|
</emItem>
|
||||||
|
<emItem blockID="i496" id="{ACAA314B-EEBA-48e4-AD47-84E31C44796C}">
|
||||||
|
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||||
|
</versionRange>
|
||||||
|
<prefs>
|
||||||
|
</prefs>
|
||||||
</emItem>
|
</emItem>
|
||||||
<emItem blockID="i67" id="youtube2@youtube2.com">
|
<emItem blockID="i67" id="youtube2@youtube2.com">
|
||||||
<versionRange minVersion="0" maxVersion="*">
|
<versionRange minVersion="0" maxVersion="*">
|
||||||
|
|
|
@ -188,5 +188,11 @@ var gContentPane = {
|
||||||
document.documentElement.openWindow("Browser:TranslationExceptions",
|
document.documentElement.openWindow("Browser:TranslationExceptions",
|
||||||
"chrome://browser/content/preferences/translation.xul",
|
"chrome://browser/content/preferences/translation.xul",
|
||||||
"", null);
|
"", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
openTranslationProviderAttribution: function ()
|
||||||
|
{
|
||||||
|
Components.utils.import("resource:///modules/translation/Translation.jsm");
|
||||||
|
Translation.openProviderAttribution();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -144,10 +144,16 @@
|
||||||
oncommand="gContentPane.showLanguages();"/>
|
oncommand="gContentPane.showLanguages();"/>
|
||||||
</row>
|
</row>
|
||||||
<row id="translationBox" hidden="true">
|
<row id="translationBox" hidden="true">
|
||||||
<checkbox id="translate" preference="browser.translation.detectLanguage" flex="1"
|
<hbox align="center">
|
||||||
label="&translateWebPages.label;" accesskey="&translateWebPages.accesskey;"
|
<checkbox id="translate" preference="browser.translation.detectLanguage"
|
||||||
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
label="&translateWebPages.label;." accesskey="&translateWebPages.accesskey;"
|
||||||
'browser.translation.detectLanguage');"/>
|
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
||||||
|
'browser.translation.detectLanguage');"/>
|
||||||
|
<label>Übersetzungen von</label>
|
||||||
|
<image id="translationAttributionImage" aria-label="Microsoft Translator"
|
||||||
|
onclick="gContentPane.openTranslationProviderAttribution()"
|
||||||
|
src="chrome://browser/content/microsoft-translator-attribution.png"/>
|
||||||
|
</hbox>
|
||||||
<button id="translateButton" label="&translateExceptions.label;"
|
<button id="translateButton" label="&translateExceptions.label;"
|
||||||
oncommand="gContentPane.showTranslationExceptions();"
|
oncommand="gContentPane.showTranslationExceptions();"
|
||||||
accesskey="&translateExceptions.accesskey;"/>
|
accesskey="&translateExceptions.accesskey;"/>
|
||||||
|
|
|
@ -186,5 +186,11 @@ var gContentPane = {
|
||||||
{
|
{
|
||||||
openDialog("chrome://browser/content/preferences/translation.xul",
|
openDialog("chrome://browser/content/preferences/translation.xul",
|
||||||
"Browser:TranslationExceptions", null);
|
"Browser:TranslationExceptions", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
openTranslationProviderAttribution: function ()
|
||||||
|
{
|
||||||
|
Components.utils.import("resource:///modules/translation/Translation.jsm");
|
||||||
|
Translation.openProviderAttribution();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -134,10 +134,17 @@
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
<hbox id="translationBox" hidden="true">
|
<hbox id="translationBox" hidden="true">
|
||||||
<checkbox id="translate" preference="browser.translation.detectLanguage" flex="1"
|
<hbox align="center" flex="1">
|
||||||
label="&translateWebPages.label;" accesskey="&translateWebPages.accesskey;"
|
<checkbox id="translate" preference="browser.translation.detectLanguage"
|
||||||
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
label="&translateWebPages.label;." accesskey="&translateWebPages.accesskey;"
|
||||||
'browser.translation.detectLanguage');"/>
|
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
||||||
|
'browser.translation.detectLanguage');"/>
|
||||||
|
<label>Übersetzungen von</label>
|
||||||
|
<separator orient="vertical" class="thin"/>
|
||||||
|
<image id="translationAttributionImage" aria-label="Microsoft Translator"
|
||||||
|
onclick="gContentPane.openTranslationProviderAttribution()"
|
||||||
|
src="chrome://browser/content/microsoft-translator-attribution.png"/>
|
||||||
|
</hbox>
|
||||||
<button id="translateButton" label="&translateExceptions.label;"
|
<button id="translateButton" label="&translateExceptions.label;"
|
||||||
oncommand="gContentPane.showTranslationExceptions();"
|
oncommand="gContentPane.showTranslationExceptions();"
|
||||||
accesskey="&translateExceptions.accesskey;"/>
|
accesskey="&translateExceptions.accesskey;"/>
|
||||||
|
|
|
@ -80,6 +80,12 @@ this.Translation = {
|
||||||
|
|
||||||
if (trUI.shouldShowInfoBar(aBrowser.currentURI))
|
if (trUI.shouldShowInfoBar(aBrowser.currentURI))
|
||||||
trUI.showTranslationInfoBar();
|
trUI.showTranslationInfoBar();
|
||||||
|
},
|
||||||
|
|
||||||
|
openProviderAttribution: function() {
|
||||||
|
Cu.import("resource:///modules/RecentWindow.jsm");
|
||||||
|
RecentWindow.getMostRecentBrowserWindow().openUILinkIn(
|
||||||
|
"http://aka.ms/MicrosoftTranslatorAttribution", "tab");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -305,6 +311,13 @@ let TranslationHealthReport = {
|
||||||
this._withProvider(provider => provider.recordLanguageChange(beforeFirstTranslation));
|
this._withProvider(provider => provider.recordLanguageChange(beforeFirstTranslation));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a denied translation offer.
|
||||||
|
*/
|
||||||
|
recordDeniedTranslationOffer: function () {
|
||||||
|
this._withProvider(provider => provider.recordDeniedTranslationOffer());
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the translation provider and pass it to the given function.
|
* Retrieve the translation provider and pass it to the given function.
|
||||||
*
|
*
|
||||||
|
@ -363,6 +376,7 @@ TranslationMeasurement1.prototype = Object.freeze({
|
||||||
pageTranslatedCountsByLanguage: DAILY_LAST_TEXT_FIELD,
|
pageTranslatedCountsByLanguage: DAILY_LAST_TEXT_FIELD,
|
||||||
detectedLanguageChangedBefore: DAILY_COUNTER_FIELD,
|
detectedLanguageChangedBefore: DAILY_COUNTER_FIELD,
|
||||||
detectedLanguageChangedAfter: DAILY_COUNTER_FIELD,
|
detectedLanguageChangedAfter: DAILY_COUNTER_FIELD,
|
||||||
|
deniedTranslationOffer: DAILY_COUNTER_FIELD,
|
||||||
detectLanguageEnabled: DAILY_LAST_NUMERIC_FIELD,
|
detectLanguageEnabled: DAILY_LAST_NUMERIC_FIELD,
|
||||||
showTranslationUI: DAILY_LAST_NUMERIC_FIELD,
|
showTranslationUI: DAILY_LAST_NUMERIC_FIELD,
|
||||||
},
|
},
|
||||||
|
@ -499,6 +513,15 @@ TranslationProvider.prototype = Object.freeze({
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
recordDeniedTranslationOffer: function () {
|
||||||
|
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
||||||
|
TranslationMeasurement1.prototype.version);
|
||||||
|
|
||||||
|
return this._enqueueTelemetryStorageTask(function* recordTask() {
|
||||||
|
yield m.incrementDailyCounter("deniedTranslationOffer");
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
collectDailyData: function () {
|
collectDailyData: function () {
|
||||||
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
let m = this.getMeasurement(TranslationMeasurement1.prototype.name,
|
||||||
TranslationMeasurement1.prototype.version);
|
TranslationMeasurement1.prototype.version);
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
browser.jar:
|
browser.jar:
|
||||||
content/browser/translation-infobar.xml
|
content/browser/translation-infobar.xml
|
||||||
|
content/browser/microsoft-translator-attribution.png
|
||||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.3 KiB |
|
@ -196,6 +196,28 @@ add_task(function* test_record_translation() {
|
||||||
yield storage.close();
|
yield storage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(function* test_denied_translation_offer() {
|
||||||
|
let storage = yield Metrics.Storage("translation");
|
||||||
|
let provider = new TranslationProvider();
|
||||||
|
yield provider.init(storage);
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
|
||||||
|
let m = provider.getMeasurement("translation", 1);
|
||||||
|
let values = yield m.getValues();
|
||||||
|
Assert.equal(values.days.size, 1);
|
||||||
|
Assert.ok(values.days.hasDay(now));
|
||||||
|
let day = values.days.getDay(now);
|
||||||
|
|
||||||
|
Assert.ok(day.has("deniedTranslationOffer"));
|
||||||
|
Assert.equal(day.get("deniedTranslationOffer"), 2);
|
||||||
|
|
||||||
|
yield provider.shutdown();
|
||||||
|
yield storage.close();
|
||||||
|
});
|
||||||
|
|
||||||
add_task(function* test_collect_daily() {
|
add_task(function* test_collect_daily() {
|
||||||
let storage = yield Metrics.Storage("translation");
|
let storage = yield Metrics.Storage("translation");
|
||||||
let provider = new TranslationProvider();
|
let provider = new TranslationProvider();
|
||||||
|
@ -253,6 +275,8 @@ add_task(function* test_healthreporter_json() {
|
||||||
yield provider.recordTranslationOpportunity("es", now);
|
yield provider.recordTranslationOpportunity("es", now);
|
||||||
yield provider.recordTranslation("es", "en", 1000, now);
|
yield provider.recordTranslation("es", "en", 1000, now);
|
||||||
|
|
||||||
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
|
||||||
yield reporter.collectMeasurements();
|
yield reporter.collectMeasurements();
|
||||||
let payload = yield reporter.getJSONPayload(true);
|
let payload = yield reporter.getJSONPayload(true);
|
||||||
let today = reporter._formatDate(now);
|
let today = reporter._formatDate(now);
|
||||||
|
@ -285,6 +309,9 @@ add_task(function* test_healthreporter_json() {
|
||||||
Assert.equal(translations["detectedLanguageChangedBefore"], 1);
|
Assert.equal(translations["detectedLanguageChangedBefore"], 1);
|
||||||
Assert.ok("detectedLanguageChangedAfter" in translations);
|
Assert.ok("detectedLanguageChangedAfter" in translations);
|
||||||
Assert.equal(translations["detectedLanguageChangedAfter"], 1);
|
Assert.equal(translations["detectedLanguageChangedAfter"], 1);
|
||||||
|
|
||||||
|
Assert.ok("deniedTranslationOffer" in translations);
|
||||||
|
Assert.equal(translations["deniedTranslationOffer"], 1);
|
||||||
} finally {
|
} finally {
|
||||||
reporter._shutdown();
|
reporter._shutdown();
|
||||||
}
|
}
|
||||||
|
@ -309,6 +336,8 @@ add_task(function* test_healthreporter_json2() {
|
||||||
yield provider.recordTranslationOpportunity("es", now);
|
yield provider.recordTranslationOpportunity("es", now);
|
||||||
yield provider.recordTranslation("es", "en", 1000, now);
|
yield provider.recordTranslation("es", "en", 1000, now);
|
||||||
|
|
||||||
|
yield provider.recordDeniedTranslationOffer();
|
||||||
|
|
||||||
yield reporter.collectMeasurements();
|
yield reporter.collectMeasurements();
|
||||||
let payload = yield reporter.getJSONPayload(true);
|
let payload = yield reporter.getJSONPayload(true);
|
||||||
let today = reporter._formatDate(now);
|
let today = reporter._formatDate(now);
|
||||||
|
@ -327,6 +356,7 @@ add_task(function* test_healthreporter_json2() {
|
||||||
Assert.ok(!("pageTranslatedCountsByLanguage" in translations));
|
Assert.ok(!("pageTranslatedCountsByLanguage" in translations));
|
||||||
Assert.ok(!("detectedLanguageChangedBefore" in translations));
|
Assert.ok(!("detectedLanguageChangedBefore" in translations));
|
||||||
Assert.ok(!("detectedLanguageChangedAfter" in translations));
|
Assert.ok(!("detectedLanguageChangedAfter" in translations));
|
||||||
|
Assert.ok(!("deniedTranslationOffer" in translations));
|
||||||
} finally {
|
} finally {
|
||||||
reporter._shutdown();
|
reporter._shutdown();
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,9 @@
|
||||||
class="translate-infobar-element options-menu-button"
|
class="translate-infobar-element options-menu-button"
|
||||||
anonid="options"
|
anonid="options"
|
||||||
label="&translation.options.menu;">
|
label="&translation.options.menu;">
|
||||||
<xul:menupopup onpopupshowing="document.getBindingParent(this).optionsShowing();">
|
<xul:menupopup class="translation-menupopup cui-widget-panel cui-widget-panelview
|
||||||
|
cui-widget-panelWithFooter PanelUI-subView"
|
||||||
|
onpopupshowing="document.getBindingParent(this).optionsShowing();">
|
||||||
<xul:menuitem anonid="neverForLanguage"
|
<xul:menuitem anonid="neverForLanguage"
|
||||||
oncommand="document.getBindingParent(this).neverForLanguage();"/>
|
oncommand="document.getBindingParent(this).neverForLanguage();"/>
|
||||||
<xul:menuitem anonid="neverForSite"
|
<xul:menuitem anonid="neverForSite"
|
||||||
|
@ -109,6 +111,12 @@
|
||||||
<xul:menuitem oncommand="openPreferences('paneContent');"
|
<xul:menuitem oncommand="openPreferences('paneContent');"
|
||||||
label="&translation.options.preferences.label;"
|
label="&translation.options.preferences.label;"
|
||||||
accesskey="&translation.options.preferences.accesskey;"/>
|
accesskey="&translation.options.preferences.accesskey;"/>
|
||||||
|
<xul:menuitem class="translation-attribution subviewbutton panel-subview-footer"
|
||||||
|
oncommand="document.getBindingParent(this).openProviderAttribution();">
|
||||||
|
<xul:label>Übersetzungen von</xul:label>
|
||||||
|
<xul:image src="chrome://browser/content/microsoft-translator-attribution.png"
|
||||||
|
aria-label="Microsoft Translator"/>
|
||||||
|
</xul:menuitem>
|
||||||
</xul:menupopup>
|
</xul:menupopup>
|
||||||
</xul:button>
|
</xul:button>
|
||||||
|
|
||||||
|
@ -310,6 +318,14 @@
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name="openProviderAttribution">
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
Translation.openProviderAttribution();
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
</implementation>
|
</implementation>
|
||||||
</binding>
|
</binding>
|
||||||
</bindings>
|
</bindings>
|
||||||
|
|
|
@ -54,6 +54,12 @@ label.small {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Content Pane */
|
||||||
|
#translationAttributionImage {
|
||||||
|
width: 70px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Applications Pane */
|
/* Applications Pane */
|
||||||
#BrowserPreferences[animated="true"] #handlersView {
|
#BrowserPreferences[animated="true"] #handlersView {
|
||||||
height: 25em;
|
height: 25em;
|
||||||
|
|
|
@ -140,6 +140,11 @@ caption {
|
||||||
border-bottom: 1px solid #ccc;
|
border-bottom: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#translationAttributionImage {
|
||||||
|
width: 70px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
#browserUseCurrent,
|
#browserUseCurrent,
|
||||||
#browserUseBookmark,
|
#browserUseBookmark,
|
||||||
#browserUseBlank {
|
#browserUseBlank {
|
||||||
|
|
|
@ -46,3 +46,21 @@ notification[value="translation"] menulist > .menulist-dropmarker {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.translation-menupopup arrowscrollbox {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-attribution {
|
||||||
|
cursor: pointer;
|
||||||
|
-moz-box-align: end;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-attribution > label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.translation-attribution > image {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
|
@ -2443,6 +2443,10 @@ notification[value="translation"] {
|
||||||
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.translation-menupopup {
|
||||||
|
-moz-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Bookmarks roots menu-items */
|
/* Bookmarks roots menu-items */
|
||||||
#subscribeToPageMenuitem:not([disabled]),
|
#subscribeToPageMenuitem:not([disabled]),
|
||||||
#subscribeToPageMenupopup,
|
#subscribeToPageMenupopup,
|
||||||
|
|
|
@ -53,6 +53,12 @@ label.small {
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Content Pane */
|
||||||
|
#translationAttributionImage {
|
||||||
|
width: 70px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Applications Pane */
|
/* Applications Pane */
|
||||||
#BrowserPreferences[animated="true"] #handlersView {
|
#BrowserPreferences[animated="true"] #handlersView {
|
||||||
height: 25em;
|
height: 25em;
|
||||||
|
|
|
@ -38,6 +38,7 @@ SEARCH_PATHS = [
|
||||||
'config',
|
'config',
|
||||||
'dom/bindings',
|
'dom/bindings',
|
||||||
'dom/bindings/parser',
|
'dom/bindings/parser',
|
||||||
|
'layout/tools/reftest',
|
||||||
'other-licenses/ply',
|
'other-licenses/ply',
|
||||||
'xpcom/idl-parser',
|
'xpcom/idl-parser',
|
||||||
'testing',
|
'testing',
|
||||||
|
|
|
@ -17,6 +17,7 @@ mozilla.pth:config
|
||||||
mozilla.pth:xpcom/typelib/xpt/tools
|
mozilla.pth:xpcom/typelib/xpt/tools
|
||||||
mozilla.pth:dom/bindings
|
mozilla.pth:dom/bindings
|
||||||
mozilla.pth:dom/bindings/parser
|
mozilla.pth:dom/bindings/parser
|
||||||
|
mozilla.pth:layout/tools/reftest
|
||||||
moztreedocs.pth:tools/docs
|
moztreedocs.pth:tools/docs
|
||||||
copy:build/buildconfig.py
|
copy:build/buildconfig.py
|
||||||
packages.txt:testing/mozbase/packages.txt
|
packages.txt:testing/mozbase/packages.txt
|
||||||
|
|
|
@ -1316,6 +1316,12 @@ this.DOMApplicationRegistry = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure we don't send additional errors for this download
|
||||||
|
app.isCanceling = true;
|
||||||
|
|
||||||
|
// Ensure this app can be downloaded again after canceling
|
||||||
|
app.downloading = false;
|
||||||
|
|
||||||
this._saveApps().then(() => {
|
this._saveApps().then(() => {
|
||||||
this.broadcastMessage("Webapps:UpdateState", {
|
this.broadcastMessage("Webapps:UpdateState", {
|
||||||
app: {
|
app: {
|
||||||
|
@ -1339,6 +1345,7 @@ this.DOMApplicationRegistry = {
|
||||||
|
|
||||||
let id = this._appIdForManifestURL(aManifestURL);
|
let id = this._appIdForManifestURL(aManifestURL);
|
||||||
let app = this.webapps[id];
|
let app = this.webapps[id];
|
||||||
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
debug("startDownload: No app found for " + aManifestURL);
|
debug("startDownload: No app found for " + aManifestURL);
|
||||||
throw new Error("NO_SUCH_APP");
|
throw new Error("NO_SUCH_APP");
|
||||||
|
@ -2770,6 +2777,14 @@ this.DOMApplicationRegistry = {
|
||||||
// initialize the progress to 0 right now
|
// initialize the progress to 0 right now
|
||||||
oldApp.progress = 0;
|
oldApp.progress = 0;
|
||||||
|
|
||||||
|
// Save the current state of the app to handle cases where we may be
|
||||||
|
// retrying a past download.
|
||||||
|
yield DOMApplicationRegistry._saveApps();
|
||||||
|
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
||||||
|
app: oldApp,
|
||||||
|
manifestURL: aNewApp.manifestURL
|
||||||
|
});
|
||||||
|
|
||||||
let zipFile = yield this._getPackage(requestChannel, id, oldApp, aNewApp);
|
let zipFile = yield this._getPackage(requestChannel, id, oldApp, aNewApp);
|
||||||
let hash = yield this._computeFileHash(zipFile.path);
|
let hash = yield this._computeFileHash(zipFile.path);
|
||||||
|
|
||||||
|
@ -4083,6 +4098,15 @@ AppcacheObserver.prototype = {
|
||||||
let setError = function appObs_setError(aError) {
|
let setError = function appObs_setError(aError) {
|
||||||
debug("Offlinecache setError to " + aError);
|
debug("Offlinecache setError to " + aError);
|
||||||
app.downloading = false;
|
app.downloading = false;
|
||||||
|
mustSave = true;
|
||||||
|
|
||||||
|
// If we are canceling the download, we already send a DOWNLOAD_CANCELED
|
||||||
|
// error.
|
||||||
|
if (app.isCanceling) {
|
||||||
|
delete app.isCanceling;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
DOMApplicationRegistry.broadcastMessage("Webapps:UpdateState", {
|
||||||
app: app,
|
app: app,
|
||||||
error: aError,
|
error: aError,
|
||||||
|
@ -4092,7 +4116,6 @@ AppcacheObserver.prototype = {
|
||||||
eventType: "downloaderror",
|
eventType: "downloaderror",
|
||||||
manifestURL: app.manifestURL
|
manifestURL: app.manifestURL
|
||||||
});
|
});
|
||||||
mustSave = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (aState) {
|
switch (aState) {
|
||||||
|
|
|
@ -699,7 +699,10 @@ BluetoothAdapter::EnableDisable(bool aEnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsTArray<nsString> types;
|
nsTArray<nsString> types;
|
||||||
types.AppendElement(NS_LITERAL_STRING("State"));
|
BT_APPEND_ENUM_STRING(types,
|
||||||
|
BluetoothAdapterAttribute,
|
||||||
|
BluetoothAdapterAttribute::State);
|
||||||
|
|
||||||
DispatchAttributeEvent(types);
|
DispatchAttributeEvent(types);
|
||||||
|
|
||||||
nsRefPtr<BluetoothReplyRunnable> result =
|
nsRefPtr<BluetoothReplyRunnable> result =
|
||||||
|
@ -792,7 +795,7 @@ BluetoothAdapter::HandlePropertyChanged(const BluetoothValue& aValue)
|
||||||
// BluetoothAdapterAttribute properties
|
// BluetoothAdapterAttribute properties
|
||||||
if (IsAdapterAttributeChanged(type, arr[i].value())) {
|
if (IsAdapterAttributeChanged(type, arr[i].value())) {
|
||||||
SetPropertyByValue(arr[i]);
|
SetPropertyByValue(arr[i]);
|
||||||
types.AppendElement(arr[i].name());
|
BT_APPEND_ENUM_STRING(types, BluetoothAdapterAttribute, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,18 @@ extern bool gBluetoothDebugFlag;
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an enum value to string then append it to an array.
|
||||||
|
*/
|
||||||
|
#define BT_APPEND_ENUM_STRING(array, enumType, enumValue) \
|
||||||
|
do { \
|
||||||
|
uint32_t index = uint32_t(enumValue); \
|
||||||
|
nsAutoString name; \
|
||||||
|
name.AssignASCII(enumType##Values::strings[index].value, \
|
||||||
|
enumType##Values::strings[index].length); \
|
||||||
|
array.AppendElement(name); \
|
||||||
|
} while(0) \
|
||||||
|
|
||||||
#define BEGIN_BLUETOOTH_NAMESPACE \
|
#define BEGIN_BLUETOOTH_NAMESPACE \
|
||||||
namespace mozilla { namespace dom { namespace bluetooth {
|
namespace mozilla { namespace dom { namespace bluetooth {
|
||||||
#define END_BLUETOOTH_NAMESPACE \
|
#define END_BLUETOOTH_NAMESPACE \
|
||||||
|
|
|
@ -260,7 +260,9 @@ BluetoothManager::DispatchAttributeEvent()
|
||||||
JSAutoCompartment ac(cx, scope);
|
JSAutoCompartment ac(cx, scope);
|
||||||
|
|
||||||
nsTArray<nsString> types;
|
nsTArray<nsString> types;
|
||||||
types.AppendElement(NS_LITERAL_STRING("DefaultAdapter"));
|
BT_APPEND_ENUM_STRING(types,
|
||||||
|
BluetoothManagerAttribute,
|
||||||
|
BluetoothManagerAttribute::DefaultAdapter);
|
||||||
|
|
||||||
if (!ToJSValue(cx, types, &value)) {
|
if (!ToJSValue(cx, types, &value)) {
|
||||||
JS_ClearPendingException(cx);
|
JS_ClearPendingException(cx);
|
||||||
|
|
|
@ -566,7 +566,7 @@ function sendMMI(aMmi) {
|
||||||
* Query current voice privacy mode.
|
* Query current voice privacy mode.
|
||||||
*
|
*
|
||||||
* Fulfill params:
|
* Fulfill params:
|
||||||
A boolean indicates the current voice privacy mode.
|
* A boolean indicates the current voice privacy mode.
|
||||||
* Reject params:
|
* Reject params:
|
||||||
* 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'.
|
* 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'.
|
||||||
*
|
*
|
||||||
|
@ -578,6 +578,54 @@ function sendMMI(aMmi) {
|
||||||
.then(() => request.result, () => { throw request.error });
|
.then(() => request.result, () => { throw request.error });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures call barring options.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
* Reject params:
|
||||||
|
* 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter' or
|
||||||
|
* 'GenericFailure'.
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function setCallBarringOption(aOptions) {
|
||||||
|
let request = mobileConnection.setCallBarringOption(aOptions);
|
||||||
|
return wrapDomRequestAsPromise(request)
|
||||||
|
.then(null, () => { throw request.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries current call barring status.
|
||||||
|
*
|
||||||
|
* Fulfill params:
|
||||||
|
* An object contains call barring status.
|
||||||
|
* Reject params:
|
||||||
|
* 'RadioNotAvailable', 'RequestNotSupported', 'InvalidParameter' or
|
||||||
|
* 'GenericFailure'.
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function getCallBarringOption(aOptions) {
|
||||||
|
let request = mobileConnection.getCallBarringOption(aOptions);
|
||||||
|
return wrapDomRequestAsPromise(request)
|
||||||
|
.then(() => request.result, () => { throw request.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change call barring facility password.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
* Reject params:
|
||||||
|
* 'RadioNotAvailable', 'RequestNotSupported', or 'GenericFailure'.
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function changeCallBarringPassword(aOptions) {
|
||||||
|
let request = mobileConnection.changeCallBarringPassword(aOptions);
|
||||||
|
return wrapDomRequestAsPromise(request)
|
||||||
|
.then(null, () => { throw request.error });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set data connection enabling state and wait for "datachange" event.
|
* Set data connection enabling state and wait for "datachange" event.
|
||||||
*
|
*
|
||||||
|
@ -959,6 +1007,94 @@ function setEmulatorOperatorNamesAndWait(aOperator, aLongName, aShortName,
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set GSM signal strength.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
* Reject params: (none)
|
||||||
|
*
|
||||||
|
* @param aRssi
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function setEmulatorGsmSignalStrength(aRssi) {
|
||||||
|
let cmd = "gsm signal " + aRssi;
|
||||||
|
return runEmulatorCmdSafe(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set emulator GSM signal strength and wait for voice and/or data state change.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
*
|
||||||
|
* @param aRssi
|
||||||
|
* @param aWaitVoice [optional]
|
||||||
|
* A boolean value. Default true.
|
||||||
|
* @param aWaitData [optional]
|
||||||
|
* A boolean value. Default false.
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function setEmulatorGsmSignalStrengthAndWait(aRssi,
|
||||||
|
aWaitVoice = true,
|
||||||
|
aWaitData = false) {
|
||||||
|
let promises = [];
|
||||||
|
if (aWaitVoice) {
|
||||||
|
promises.push(waitForManagerEvent("voicechange"));
|
||||||
|
}
|
||||||
|
if (aWaitData) {
|
||||||
|
promises.push(waitForManagerEvent("datachange"));
|
||||||
|
}
|
||||||
|
promises.push(setEmulatorGsmSignalStrength(aRssi));
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set LTE signal strength.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
* Reject params: (none)
|
||||||
|
*
|
||||||
|
* @param aRxlev
|
||||||
|
* @param aRsrp
|
||||||
|
* @param aRssnr
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function setEmulatorLteSignalStrength(aRxlev, aRsrp, aRssnr) {
|
||||||
|
let cmd = "gsm lte_signal " + aRxlev + " " + aRsrp + " " + aRssnr;
|
||||||
|
return runEmulatorCmdSafe(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set emulator LTE signal strength and wait for voice and/or data state change.
|
||||||
|
*
|
||||||
|
* Fulfill params: (none)
|
||||||
|
*
|
||||||
|
* @param aRxlev
|
||||||
|
* @param aRsrp
|
||||||
|
* @param aRssnr
|
||||||
|
* @param aWaitVoice [optional]
|
||||||
|
* A boolean value. Default true.
|
||||||
|
* @param aWaitData [optional]
|
||||||
|
* A boolean value. Default false.
|
||||||
|
*
|
||||||
|
* @return A deferred promise.
|
||||||
|
*/
|
||||||
|
function setEmulatorLteSignalStrengthAndWait(aRxlev, aRsrp, aRssnr,
|
||||||
|
aWaitVoice = true,
|
||||||
|
aWaitData = false) {
|
||||||
|
let promises = [];
|
||||||
|
if (aWaitVoice) {
|
||||||
|
promises.push(waitForManagerEvent("voicechange"));
|
||||||
|
}
|
||||||
|
if (aWaitData) {
|
||||||
|
promises.push(waitForManagerEvent("datachange"));
|
||||||
|
}
|
||||||
|
promises.push(setEmulatorLteSignalStrength(aRxlev, aRsrp, aRssnr));
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
let _networkManager;
|
let _networkManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
|
||||||
|
|
||||||
// In single sim scenario, there is only one mobileConnection, we can always use
|
|
||||||
// the first instance.
|
|
||||||
let mobileConnection = window.navigator.mozMobileConnections[0];
|
|
||||||
ok(mobileConnection instanceof MozMobileConnection,
|
|
||||||
"mobileConnection is instanceof " + mobileConnection.constructor);
|
|
||||||
|
|
||||||
let _pendingEmulatorCmdCount = 0;
|
|
||||||
|
|
||||||
/* Remove permission and execute finish() */
|
|
||||||
let cleanUp = function() {
|
|
||||||
waitFor(function() {
|
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
|
||||||
finish();
|
|
||||||
}, function() {
|
|
||||||
return _pendingEmulatorCmdCount === 0;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Helper for tasks */
|
|
||||||
let taskHelper = {
|
|
||||||
tasks: [],
|
|
||||||
|
|
||||||
push: function(task) {
|
|
||||||
this.tasks.push(task);
|
|
||||||
},
|
|
||||||
|
|
||||||
runNext: function() {
|
|
||||||
let task = this.tasks.shift();
|
|
||||||
if (!task) {
|
|
||||||
cleanUp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof task === "function") {
|
|
||||||
task();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Helper for emulator console command */
|
|
||||||
let emulatorHelper = {
|
|
||||||
sendCommand: function(cmd, callback) {
|
|
||||||
_pendingEmulatorCmdCount++;
|
|
||||||
runEmulatorCmd(cmd, function(results) {
|
|
||||||
_pendingEmulatorCmdCount--;
|
|
||||||
|
|
||||||
let result = results[results.length - 1];
|
|
||||||
is(result, "OK", "'"+ cmd +"' returns '" + result + "'");
|
|
||||||
|
|
||||||
if (callback && typeof callback === "function") {
|
|
||||||
callback(results);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -2,62 +2,49 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 60000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
const TEST_DATA = [
|
||||||
|
// [<pin>, <new pin>, <expected error>]
|
||||||
|
|
||||||
// Permission changes can't change existing Navigator.prototype
|
// Test passing an invalid pin or newPin.
|
||||||
// objects, so grab our objects from a new Navigator
|
[null, "0000", "InvalidPassword"],
|
||||||
let ifr = document.createElement("iframe");
|
["0000", null, "InvalidPassword"],
|
||||||
let connection;
|
[null, null, "InvalidPassword"],
|
||||||
ifr.onload = function() {
|
|
||||||
connection = ifr.contentWindow.navigator.mozMobileConnections[0];
|
|
||||||
|
|
||||||
ok(connection instanceof ifr.contentWindow.MozMobileConnection,
|
// Test passing mismatched newPin.
|
||||||
"connection is instanceof " + connection.constructor);
|
["000", "0000", "InvalidPassword"],
|
||||||
|
["00000", "1111", "InvalidPassword"],
|
||||||
|
["abcd", "efgh", "InvalidPassword"],
|
||||||
|
|
||||||
setTimeout(testChangeCallBarringPasswordWithFailure, 0);
|
// TODO: Bug 906603 - B2G RIL: Support Change Call Barring Password on Emulator.
|
||||||
};
|
// Currently emulator doesn't support REQUEST_CHANGE_BARRING_PASSWORD, so we
|
||||||
document.body.appendChild(ifr);
|
// expect to get a 'RequestNotSupported' error here.
|
||||||
|
["1234", "1234", "RequestNotSupported"]
|
||||||
|
];
|
||||||
|
|
||||||
function testChangeCallBarringPasswordWithFailure() {
|
function testChangeCallBarringPassword(aPin, aNewPin, aExpectedError) {
|
||||||
// Incorrect parameters, expect onerror callback.
|
log("Test changing call barring password to " + aPin + "/" + aNewPin);
|
||||||
let options = [
|
|
||||||
{pin: null, newPin: '0000'},
|
|
||||||
{pin: '0000', newPin: null},
|
|
||||||
{pin: null, newPin: null},
|
|
||||||
{pin: '000', newPin: '0000'},
|
|
||||||
{pin: '00000', newPin: '1111'},
|
|
||||||
{pin: 'abcd', newPin: 'efgh'},
|
|
||||||
];
|
|
||||||
|
|
||||||
function do_test() {
|
let options = {
|
||||||
for (let i = 0; i < options.length; i++) {
|
pin: aPin,
|
||||||
let request = connection.changeCallBarringPassword(options[i]);
|
newPin: aNewPin
|
||||||
|
};
|
||||||
|
return changeCallBarringPassword(options)
|
||||||
|
.then(function resolve() {
|
||||||
|
ok(!aExpectedError, "changeCallBarringPassword success");
|
||||||
|
}, function reject(aError) {
|
||||||
|
is(aError.name, aExpectedError, "failed to changeCallBarringPassword");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
request.onsuccess = function() {
|
// Start tests
|
||||||
ok(false, 'Unexpected result.');
|
startTestCommon(function() {
|
||||||
setTimeout(cleanUp , 0);
|
let promise = Promise.resolve();
|
||||||
};
|
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||||
|
let data = TEST_DATA[i];
|
||||||
request.onerror = function() {
|
promise =
|
||||||
ok(request.error.name === 'InvalidPassword', 'InvalidPassword');
|
promise.then(() => testChangeCallBarringPassword(data[0], data[1], data[2]));
|
||||||
if (i >= options.length) {
|
|
||||||
setTimeout(testChangeCallBarringPasswordWithSuccess, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return promise;
|
||||||
do_test();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function testChangeCallBarringPasswordWithSuccess() {
|
|
||||||
// TODO: Bug 906603 - B2G RIL: Support Change Call Barring Password on
|
|
||||||
// Emulator.
|
|
||||||
setTimeout(cleanUp , 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanUp() {
|
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,38 +2,73 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 60000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
const TEST_DATA = [
|
||||||
|
// Test passing invalid program.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
program: 5, /* Invalid program */
|
||||||
|
serviceClass: 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
program: null,
|
||||||
|
serviceClass: 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
/* Undefined program */
|
||||||
|
serviceClass: 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
},
|
||||||
|
// Test passing invalid serviceClass.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
program: MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
serviceClass: null
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
program: MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
/* Undefined serviceClass */
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
},
|
||||||
|
// TODO: Bug 1027546 - [B2G][Emulator] Support call barring
|
||||||
|
// Currently emulator doesn't support call barring, so we expect to get a
|
||||||
|
// 'RequestNotSupported' error here.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
program: MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
serviceClass: 0
|
||||||
|
},
|
||||||
|
expectedError: "RequestNotSupported"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
// Permission changes can't change existing Navigator.prototype
|
function testGetCallBarringOption(aOptions, aExpectedError) {
|
||||||
// objects, so grab our objects from a new Navigator
|
log("Test getting call barring to " + JSON.stringify(aOptions));
|
||||||
let ifr = document.createElement("iframe");
|
|
||||||
let connection;
|
|
||||||
ifr.onload = function() {
|
|
||||||
connection = ifr.contentWindow.navigator.mozMobileConnections[0];
|
|
||||||
|
|
||||||
ok(connection instanceof ifr.contentWindow.MozMobileConnection,
|
return getCallBarringOption(aOptions)
|
||||||
"connection is instanceof " + connection.constructor);
|
.then(function resolve(aResult) {
|
||||||
|
ok(false, "should not success");
|
||||||
testGetCallBarringOption();
|
}, function reject(aError) {
|
||||||
};
|
is(aError.name, aExpectedError, "failed to getCallBarringOption");
|
||||||
document.body.appendChild(ifr);
|
});
|
||||||
|
|
||||||
function testGetCallBarringOption() {
|
|
||||||
let option = {'program': 0, 'password': '', 'serviceClass': 0};
|
|
||||||
let request = connection.getCallBarringOption(option);
|
|
||||||
request.onsuccess = function() {
|
|
||||||
ok(request.result);
|
|
||||||
ok('enabled' in request.result, 'should have "enabled" field');
|
|
||||||
cleanUp();
|
|
||||||
};
|
|
||||||
request.onerror = function() {
|
|
||||||
// Call barring is not supported by current emulator.
|
|
||||||
cleanUp();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanUp() {
|
// Start tests
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
startTestCommon(function() {
|
||||||
finish();
|
let promise = Promise.resolve();
|
||||||
}
|
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||||
|
let data = TEST_DATA[i];
|
||||||
|
promise = promise.then(() => testGetCallBarringOption(data.options,
|
||||||
|
data.expectedError));
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
});
|
||||||
|
|
|
@ -2,73 +2,121 @@
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 60000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
const TEST_DATA = [
|
||||||
|
// Test passing invalid program.
|
||||||
// Permission changes can't change existing Navigator.prototype
|
{
|
||||||
// objects, so grab our objects from a new Navigator
|
options: {
|
||||||
let ifr = document.createElement("iframe");
|
"program": 5, /* Invalid program */
|
||||||
let connection;
|
"enabled": true,
|
||||||
ifr.onload = function() {
|
"password": "0000",
|
||||||
connection = ifr.contentWindow.navigator.mozMobileConnections[0];
|
"serviceClass": 0
|
||||||
|
},
|
||||||
ok(connection instanceof ifr.contentWindow.MozMobileConnection,
|
expectedError: "InvalidParameter"
|
||||||
"connection is instanceof " + connection.constructor);
|
}, {
|
||||||
|
options: {
|
||||||
nextTest();
|
"program": null,
|
||||||
};
|
"enabled": true,
|
||||||
document.body.appendChild(ifr);
|
"password": "0000",
|
||||||
|
"serviceClass": 0
|
||||||
let caseId = 0;
|
},
|
||||||
let options = [
|
expectedError: "InvalidParameter"
|
||||||
buildOption(5, true, '0000', 0), // invalid program.
|
}, {
|
||||||
|
options: {
|
||||||
// test null.
|
/* Undefined program */
|
||||||
buildOption(null, true, '0000', 0),
|
"enabled": true,
|
||||||
buildOption(0, null, '0000', 0),
|
"password": "0000",
|
||||||
buildOption(0, true, null, 0),
|
"serviceClass": 0
|
||||||
buildOption(0, true, '0000', null),
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
// test undefined.
|
},
|
||||||
{'enabled': true, 'password': '0000', 'serviceClass': 0},
|
// Test passing invalid enabled.
|
||||||
{'program': 0, 'password': '0000', 'serviceClass': 0},
|
{
|
||||||
{'program': 0, 'enabled': true, 'serviceClass': 0},
|
options: {
|
||||||
{'program': 0, 'enabled': true, 'password': '0000'},
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": null,
|
||||||
|
"password": "0000",
|
||||||
|
"serviceClass": 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
/* Undefined enabled */
|
||||||
|
"password": "0000",
|
||||||
|
"serviceClass": 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
},
|
||||||
|
// Test passing invalid password.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": true,
|
||||||
|
"password": null,
|
||||||
|
"serviceClass": 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": true,
|
||||||
|
/* Undefined password */
|
||||||
|
"serviceClass": 0
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
},
|
||||||
|
// Test passing invalid serviceClass.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": true,
|
||||||
|
"password": "0000",
|
||||||
|
"serviceClass": null
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
}, {
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": true,
|
||||||
|
"password": "0000",
|
||||||
|
/* Undefined serviceClass */
|
||||||
|
},
|
||||||
|
expectedError: "InvalidParameter"
|
||||||
|
},
|
||||||
|
// TODO: Bug 1027546 - [B2G][Emulator] Support call barring
|
||||||
|
// Currently emulator doesn't support call barring, so we expect to get a
|
||||||
|
// 'RequestNotSupported' error here.
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
"program": MozMobileConnection.CALL_BARRING_PROGRAM_ALL_OUTGOING,
|
||||||
|
"enabled": true,
|
||||||
|
"password": "0000",
|
||||||
|
"serviceClass": 0
|
||||||
|
},
|
||||||
|
expectedError: "RequestNotSupported"
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
function buildOption(program, enabled, password, serviceClass) {
|
function testSetCallBarringOption(aOptions, aExpectedError) {
|
||||||
return {
|
log("Test setting call barring to " + JSON.stringify(aOptions));
|
||||||
'program': program,
|
|
||||||
'enabled': enabled,
|
return setCallBarringOption(aOptions)
|
||||||
'password': password,
|
.then(function resolve() {
|
||||||
'serviceClass': serviceClass
|
ok(false, "changeCallBarringPassword success");
|
||||||
};
|
}, function reject(aError) {
|
||||||
|
is(aError.name, aExpectedError, "failed to changeCallBarringPassword");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSetCallBarringOptionError(option) {
|
// Start tests
|
||||||
let request = connection.setCallBarringOption(option);
|
startTestCommon(function() {
|
||||||
request.onsuccess = function() {
|
let promise = Promise.resolve();
|
||||||
ok(false,
|
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||||
'should not fire onsuccess for invaild call barring option: '
|
let data = TEST_DATA[i];
|
||||||
+ JSON.stringify(option));
|
promise = promise.then(() => testSetCallBarringOption(data.options,
|
||||||
};
|
data.expectedError));
|
||||||
request.onerror = function(event) {
|
|
||||||
is(event.target.error.name, 'InvalidParameter', JSON.stringify(option));
|
|
||||||
nextTest();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextTest() {
|
|
||||||
if (caseId >= options.length) {
|
|
||||||
cleanUp();
|
|
||||||
} else {
|
|
||||||
let option = options[caseId++];
|
|
||||||
log('test for ' + JSON.stringify(option));
|
|
||||||
testSetCallBarringOptionError(option);
|
|
||||||
}
|
}
|
||||||
}
|
return promise;
|
||||||
|
});
|
||||||
function cleanUp() {
|
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,84 +1,33 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 30000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
// The emulator's hard coded iccid value.
|
||||||
|
const ICCID = "89014103211118510720";
|
||||||
|
|
||||||
// Permission changes can't change existing Navigator.prototype
|
function setRadioEnabledAndWaitIccChange(aEnabled) {
|
||||||
// objects, so grab our objects from a new Navigator
|
let promises = [];
|
||||||
let ifr = document.createElement("iframe");
|
promises.push(waitForManagerEvent("iccchange"));
|
||||||
let connection;
|
promises.push(setRadioEnabled(aEnabled));
|
||||||
ifr.onload = function() {
|
|
||||||
connection = ifr.contentWindow.navigator.mozMobileConnections[0];
|
|
||||||
ok(connection instanceof ifr.contentWindow.MozMobileConnection,
|
|
||||||
"connection is instanceof " + connection.constructor);
|
|
||||||
|
|
||||||
// The emulator's hard coded iccid value.
|
return Promise.all(promises);
|
||||||
// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c.
|
|
||||||
is(connection.iccId, 89014103211118510720);
|
|
||||||
|
|
||||||
runNextTest();
|
|
||||||
};
|
|
||||||
document.body.appendChild(ifr);
|
|
||||||
|
|
||||||
function waitForIccChange(callback) {
|
|
||||||
connection.addEventListener("iccchange", function handler() {
|
|
||||||
connection.removeEventListener("iccchange", handler);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRadioEnabled(enabled) {
|
// Start tests
|
||||||
let request = connection.setRadioEnabled(enabled);
|
startTestCommon(function() {
|
||||||
|
log("Test initial iccId");
|
||||||
|
is(mobileConnection.iccId, ICCID);
|
||||||
|
|
||||||
request.onsuccess = function onsuccess() {
|
return setRadioEnabledAndWaitIccChange(false)
|
||||||
log('setRadioEnabled: ' + enabled);
|
.then(() => {
|
||||||
};
|
is(mobileConnection.iccId, null);
|
||||||
|
})
|
||||||
|
|
||||||
request.onerror = function onerror() {
|
// Restore radio state.
|
||||||
ok(false, "setRadioEnabled should be ok");
|
.then(() => setRadioEnabledAndWaitIccChange(true))
|
||||||
};
|
.then(() => {
|
||||||
}
|
is(mobileConnection.iccId, ICCID);
|
||||||
|
});
|
||||||
function testIccChangeOnRadioPowerOff() {
|
});
|
||||||
// Turn off radio
|
|
||||||
setRadioEnabled(false);
|
|
||||||
|
|
||||||
waitForIccChange(function() {
|
|
||||||
is(connection.iccId, null);
|
|
||||||
runNextTest();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testIccChangeOnRadioPowerOn() {
|
|
||||||
// Turn on radio
|
|
||||||
setRadioEnabled(true);
|
|
||||||
|
|
||||||
waitForIccChange(function() {
|
|
||||||
// The emulator's hard coded iccid value.
|
|
||||||
is(connection.iccId, 89014103211118510720);
|
|
||||||
runNextTest();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
testIccChangeOnRadioPowerOff,
|
|
||||||
testIccChangeOnRadioPowerOn
|
|
||||||
];
|
|
||||||
|
|
||||||
function runNextTest() {
|
|
||||||
let test = tests.shift();
|
|
||||||
if (!test) {
|
|
||||||
cleanUp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanUp() {
|
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
|
||||||
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,47 +1,13 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 30000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
SpecialPowers.addPermission("mobilenetwork", true, document);
|
// Start tests
|
||||||
|
startTestCommon(function() {
|
||||||
let connection = navigator.mozMobileConnections[0];
|
|
||||||
ok(connection instanceof MozMobileConnection,
|
|
||||||
"connection is instanceof " + connection.constructor);
|
|
||||||
|
|
||||||
|
|
||||||
function testLastKnownNetwork() {
|
|
||||||
log("testLastKnownNetwork: " + connection.lastKnownNetwork);
|
|
||||||
// The emulator's hard coded operatoer's mcc and mnc codes.
|
// The emulator's hard coded operatoer's mcc and mnc codes.
|
||||||
is(connection.lastKnownNetwork, "310-260");
|
is(mobileConnection.lastKnownNetwork, "310-260");
|
||||||
runNextTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testLastKnownHomeNetwork() {
|
|
||||||
log("testLastKnownHomeNetwork: " + connection.lastKnownHomeNetwork);
|
|
||||||
// The emulator's hard coded icc's mcc and mnc codes.
|
// The emulator's hard coded icc's mcc and mnc codes.
|
||||||
is(connection.lastKnownHomeNetwork, "310-260");
|
is(mobileConnection.lastKnownHomeNetwork, "310-260");
|
||||||
runNextTest();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
let tests = [
|
|
||||||
testLastKnownNetwork,
|
|
||||||
testLastKnownHomeNetwork
|
|
||||||
];
|
|
||||||
|
|
||||||
function runNextTest() {
|
|
||||||
let test = tests.shift();
|
|
||||||
if (!test) {
|
|
||||||
cleanUp();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanUp() {
|
|
||||||
SpecialPowers.removePermission("mobilenetwork", document);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
runNextTest();
|
|
||||||
|
|
|
@ -2,167 +2,40 @@
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 60000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
const DATA_KEY = "ril.data.enabled";
|
// Start tests
|
||||||
const APN_KEY = "ril.data.apnSettings";
|
startTestCommon(function() {
|
||||||
|
|
||||||
let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
|
let origApnSettings;
|
||||||
|
return getDataApnSettings()
|
||||||
|
.then(value => {
|
||||||
|
origApnSettings = value;
|
||||||
|
})
|
||||||
|
|
||||||
SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
|
// Test disabling/enabling radio power.
|
||||||
SpecialPowers.addPermission("mobileconnection", true, document);
|
.then(() => setRadioEnabledAndWait(false))
|
||||||
SpecialPowers.addPermission("settings-read", true, document);
|
.then(() => setRadioEnabledAndWait(true))
|
||||||
SpecialPowers.addPermission("settings-write", true, document);
|
|
||||||
|
|
||||||
let settings = window.navigator.mozSettings;
|
// Test disabling radio when data is connected.
|
||||||
let connection = window.navigator.mozMobileConnections[0];
|
.then(() => {
|
||||||
ok(connection instanceof MozMobileConnection,
|
let apnSettings = [[
|
||||||
"connection is instanceof " + connection.constructor);
|
|
||||||
|
|
||||||
function setSetting(key, value) {
|
|
||||||
let deferred = Promise.defer();
|
|
||||||
|
|
||||||
let setLock = settings.createLock();
|
|
||||||
let obj = {};
|
|
||||||
obj[key] = value;
|
|
||||||
|
|
||||||
let setReq = setLock.set(obj);
|
|
||||||
setReq.addEventListener("success", function onSetSuccess() {
|
|
||||||
ok(true, "set '" + key + "' to " + obj[key]);
|
|
||||||
deferred.resolve();
|
|
||||||
});
|
|
||||||
setReq.addEventListener("error", function onSetError() {
|
|
||||||
ok(false, "cannot set '" + key + "'");
|
|
||||||
deferred.reject();
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setEmulatorAPN() {
|
|
||||||
let apn =
|
|
||||||
[
|
|
||||||
[
|
|
||||||
{"carrier":"T-Mobile US",
|
{"carrier":"T-Mobile US",
|
||||||
"apn":"epc.tmobile.com",
|
"apn":"epc.tmobile.com",
|
||||||
"mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
|
"mmsc":"http://mms.msg.eng.t-mobile.com/mms/wapenc",
|
||||||
"types":["default","supl","mms"]}
|
"types":["default","supl","mms"]}]];
|
||||||
]
|
return setDataApnSettings(apnSettings);
|
||||||
];
|
})
|
||||||
return setSetting(APN_KEY, apn);
|
.then(() => setDataEnabledAndWait(true))
|
||||||
}
|
.then(() => setRadioEnabledAndWait(false))
|
||||||
|
|
||||||
function enableData() {
|
|
||||||
log("Turn data on.");
|
|
||||||
|
|
||||||
let deferred = Promise.defer();
|
|
||||||
|
|
||||||
connection.addEventListener("datachange", function ondatachange() {
|
|
||||||
if (connection.data.connected === true) {
|
|
||||||
connection.removeEventListener("datachange", ondatachange);
|
|
||||||
log("mobileConnection.data.connected is now '"
|
|
||||||
+ connection.data.connected + "'.");
|
|
||||||
deferred.resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setEmulatorAPN()
|
|
||||||
.then(() => setSetting(DATA_KEY, true));
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function receivedPending(received, pending, nextAction) {
|
|
||||||
let index = pending.indexOf(received);
|
|
||||||
if (index != -1) {
|
|
||||||
pending.splice(index, 1);
|
|
||||||
}
|
|
||||||
if (pending.length === 0) {
|
|
||||||
nextAction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitRadioState(state) {
|
|
||||||
let deferred = Promise.defer();
|
|
||||||
|
|
||||||
waitFor(function() {
|
|
||||||
deferred.resolve();
|
|
||||||
}, function() {
|
|
||||||
return connection.radioState == state;
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setRadioEnabled(enabled, transientState, finalState) {
|
|
||||||
log("setRadioEnabled to " + enabled);
|
|
||||||
|
|
||||||
let deferred = Promise.defer();
|
|
||||||
let done = function() {
|
|
||||||
deferred.resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
let pending = ["onradiostatechange", "onsuccess"];
|
|
||||||
|
|
||||||
let receivedTransient = false;
|
|
||||||
connection.onradiostatechange = function() {
|
|
||||||
let state = connection.radioState;
|
|
||||||
log("Received 'radiostatechange' event, radioState: " + state);
|
|
||||||
|
|
||||||
if (state == transientState) {
|
|
||||||
receivedTransient = true;
|
|
||||||
} else if (state == finalState) {
|
|
||||||
ok(receivedTransient);
|
|
||||||
receivedPending("onradiostatechange", pending, done);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let req = connection.setRadioEnabled(enabled);
|
|
||||||
|
|
||||||
req.onsuccess = function() {
|
|
||||||
log("setRadioEnabled success");
|
|
||||||
receivedPending("onsuccess", pending, done);
|
|
||||||
};
|
|
||||||
|
|
||||||
req.onerror = function() {
|
|
||||||
ok(false, "setRadioEnabled should not fail");
|
|
||||||
deferred.reject();
|
|
||||||
};
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSwitchRadio() {
|
|
||||||
log("= testSwitchRadio =");
|
|
||||||
return waitRadioState("enabled")
|
|
||||||
.then(setRadioEnabled.bind(null, false, "disabling", "disabled"))
|
|
||||||
.then(setRadioEnabled.bind(null, true, "enabling", "enabled"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDisableRadioWhenDataConnected() {
|
|
||||||
log("= testDisableRadioWhenDataConnected =");
|
|
||||||
return waitRadioState("enabled")
|
|
||||||
.then(enableData)
|
|
||||||
.then(setRadioEnabled.bind(null, false, "disabling", "disabled"))
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Data should be disconnected.
|
// Data should be disconnected.
|
||||||
is(connection.data.connected, false);
|
is(mobileConnection.data.connected, false);
|
||||||
})
|
})
|
||||||
.then(setRadioEnabled.bind(null, true, "enabling", "enabled"))
|
|
||||||
// Disable data
|
|
||||||
.then(setSetting.bind(null, DATA_KEY, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanUp() {
|
// Restore test environment.
|
||||||
SpecialPowers.removePermission("mobileconnection", document);
|
.then(() => setDataApnSettings(origApnSettings))
|
||||||
SpecialPowers.removePermission("settings-write", document);
|
.then(() => setDataEnabled(false))
|
||||||
SpecialPowers.removePermission("settings-read", document);
|
.then(() => setRadioEnabledAndWait(true));
|
||||||
SpecialPowers.clearUserPref("dom.mozSettings.enabled");
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
testSwitchRadio()
|
}, ["settings-read", "settings-write"]);
|
||||||
.then(testDisableRadioWhenDataConnected)
|
|
||||||
.then(null, () => {
|
|
||||||
ok(false, "promise reject somewhere");
|
|
||||||
})
|
|
||||||
.then(cleanUp);
|
|
||||||
|
|
|
@ -1,132 +1,98 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
MARIONETTE_TIMEOUT = 30000;
|
MARIONETTE_TIMEOUT = 60000;
|
||||||
MARIONETTE_HEAD_JS = "mobile_header.js";
|
MARIONETTE_HEAD_JS = "head.js";
|
||||||
|
|
||||||
/* Emulator command for GSM/UMTS signal strength */
|
// Emulator uses rssi = 7 as default value.
|
||||||
function setEmulatorGsmSignalStrength(rssi) {
|
const DEFAULT_RSSI = 7;
|
||||||
emulatorHelper.sendCommand("gsm signal " + rssi);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Emulator command for LTE signal strength */
|
const TEST_DATA = [
|
||||||
function setEmulatorLteSignalStrength(rxlev, rsrp, rssnr) {
|
// All invalid case.
|
||||||
let lteSignal = rxlev + " " + rsrp + " " + rssnr;
|
{
|
||||||
emulatorHelper.sendCommand("gsm lte_signal " + lteSignal);
|
input: {
|
||||||
}
|
rxlev: 99,
|
||||||
|
rsrp: 65535,
|
||||||
function waitForVoiceChangeEvent(callback) {
|
rssnr: 65535
|
||||||
mobileConnection.addEventListener("voicechange", function onvoicechange() {
|
},
|
||||||
mobileConnection.removeEventListener("voicechange", onvoicechange);
|
expect: {
|
||||||
|
signalStrength: null,
|
||||||
if (callback && typeof callback === "function") {
|
relSignalStrength: null
|
||||||
callback();
|
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
}
|
// Valid rxlev with max value.
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
rxlev: 63,
|
||||||
|
rsrp: 65535,
|
||||||
|
rssnr: 65535
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
signalStrength: -48,
|
||||||
|
relSignalStrength: 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Valid rxlev.
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
rxlev: 12,
|
||||||
|
rsrp: 65535,
|
||||||
|
rssnr: 65535
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
signalStrength: -99,
|
||||||
|
relSignalStrength: 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Valid rxlev with min value.
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
rxlev: 0,
|
||||||
|
rsrp: 65535,
|
||||||
|
rssnr: 65535
|
||||||
|
},
|
||||||
|
expect: {
|
||||||
|
signalStrength: -111,
|
||||||
|
relSignalStrength: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
/* Test Initial Signal Strength Info */
|
function testInitialSignalStrengthInfo() {
|
||||||
taskHelper.push(function testInitialSignalStrengthInfo() {
|
|
||||||
log("Test initial signal strength info");
|
log("Test initial signal strength info");
|
||||||
|
|
||||||
let voice = mobileConnection.voice;
|
let voice = mobileConnection.voice;
|
||||||
// Android emulator initializes the signal strength to -99 dBm
|
// Android emulator initializes the signal strength to -99 dBm
|
||||||
is(voice.signalStrength, -99, "check voice.signalStrength");
|
is(voice.signalStrength, -99, "check voice.signalStrength");
|
||||||
is(voice.relSignalStrength, 44, "check voice.relSignalStrength");
|
is(voice.relSignalStrength, 44, "check voice.relSignalStrength");
|
||||||
|
}
|
||||||
|
|
||||||
taskHelper.runNext();
|
function testLteSignalStrength(aInput, aExpect) {
|
||||||
});
|
log("Test setting LTE signal strength to " + JSON.stringify(aInput));
|
||||||
|
|
||||||
/* Test Unsolicited Signal Strength Events for LTE */
|
return setEmulatorLteSignalStrengthAndWait(aInput.rxlev, aInput.rsrp, aInput.rssnr)
|
||||||
taskHelper.push(function testLteSignalStrength() {
|
.then(() => {
|
||||||
// Set emulator's LTE signal strength and wait for 'onvoicechange' event.
|
|
||||||
function doTestLteSignalStrength(input, expect, callback) {
|
|
||||||
log("Test LTE signal info with data : " + JSON.stringify(input));
|
|
||||||
|
|
||||||
waitForVoiceChangeEvent(function() {
|
|
||||||
let voice = mobileConnection.voice;
|
let voice = mobileConnection.voice;
|
||||||
is(voice.signalStrength, expect.signalStrength,
|
is(voice.signalStrength, aExpect.signalStrength,
|
||||||
"check voice.signalStrength");
|
"check voice.signalStrength");
|
||||||
is(voice.relSignalStrength, expect.relSignalStrength,
|
is(voice.relSignalStrength, aExpect.relSignalStrength,
|
||||||
"check voice.relSignalStrength");
|
"check voice.relSignalStrength");
|
||||||
|
|
||||||
if (callback && typeof callback === "function") {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setEmulatorLteSignalStrength(input.rxlev, input.rsrp, input.rssnr);
|
// Start tests
|
||||||
|
startTestCommon(function() {
|
||||||
|
// Test initial status
|
||||||
|
testInitialSignalStrengthInfo();
|
||||||
|
|
||||||
|
// Test Unsolicited Signal Strength Events for LTE
|
||||||
|
let promise = Promise.resolve();
|
||||||
|
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||||
|
let data = TEST_DATA[i];
|
||||||
|
promise = promise.then(() => testLteSignalStrength(data.input,
|
||||||
|
data.expect));
|
||||||
}
|
}
|
||||||
|
|
||||||
let testData = [
|
// Reset Signal Strength Info to default
|
||||||
// All invalid case.
|
return promise.then(() => setEmulatorGsmSignalStrengthAndWait(DEFAULT_RSSI));
|
||||||
{input: {
|
|
||||||
rxlev: 99,
|
|
||||||
rsrp: 65535,
|
|
||||||
rssnr: 65535},
|
|
||||||
expect: {
|
|
||||||
signalStrength: null,
|
|
||||||
relSignalStrength: null}
|
|
||||||
},
|
|
||||||
// Valid rxlev with max value.
|
|
||||||
{input: {
|
|
||||||
rxlev: 63,
|
|
||||||
rsrp: 65535,
|
|
||||||
rssnr: 65535},
|
|
||||||
expect: {
|
|
||||||
signalStrength: -48,
|
|
||||||
relSignalStrength: 100}
|
|
||||||
},
|
|
||||||
// Valid rxlev.
|
|
||||||
{input: {
|
|
||||||
rxlev: 12,
|
|
||||||
rsrp: 65535,
|
|
||||||
rssnr: 65535},
|
|
||||||
expect: {
|
|
||||||
signalStrength: -99,
|
|
||||||
relSignalStrength: 100}
|
|
||||||
},
|
|
||||||
// Valid rxlev with min value.
|
|
||||||
{input: {
|
|
||||||
rxlev: 0,
|
|
||||||
rsrp: 65535,
|
|
||||||
rssnr: 65535},
|
|
||||||
expect: {
|
|
||||||
signalStrength: -111,
|
|
||||||
relSignalStrength: 0}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// Run all test data.
|
|
||||||
(function do_call() {
|
|
||||||
let next = testData.shift();
|
|
||||||
if (!next) {
|
|
||||||
taskHelper.runNext();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
doTestLteSignalStrength(next.input, next.expect, do_call);
|
|
||||||
})();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Reset Signal Strength Info to default, and finsih the test */
|
|
||||||
taskHelper.push(function testResetSignalStrengthInfo() {
|
|
||||||
// Reset emulator's signal strength and wait for 'onvoicechange' event.
|
|
||||||
function doResetSignalStrength(rssi) {
|
|
||||||
waitForVoiceChangeEvent(function() {
|
|
||||||
let voice = mobileConnection.voice;
|
|
||||||
is(voice.signalStrength, -99, "check voice.signalStrength");
|
|
||||||
is(voice.relSignalStrength, 44, "check voice.relSignalStrength");
|
|
||||||
|
|
||||||
taskHelper.runNext();
|
|
||||||
});
|
|
||||||
|
|
||||||
setEmulatorGsmSignalStrength(rssi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulator uses rssi = 7 as default value, and we need to reset it after
|
|
||||||
// finishing test in case other test cases need those values for testing.
|
|
||||||
doResetSignalStrength(7);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start test
|
|
||||||
taskHelper.runNext();
|
|
||||||
|
|
|
@ -19,6 +19,24 @@ struct MutexData {
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
static void
|
||||||
|
InitMutex(pthread_mutex_t* mMutex)
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t mutexAttributes;
|
||||||
|
pthread_mutexattr_init(&mutexAttributes);
|
||||||
|
// Make the mutex reentrant so it behaves the same as a win32 mutex
|
||||||
|
if (pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE)) {
|
||||||
|
MOZ_CRASH();
|
||||||
|
}
|
||||||
|
if (pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED)) {
|
||||||
|
MOZ_CRASH();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(mMutex, &mutexAttributes)) {
|
||||||
|
MOZ_CRASH();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CrossProcessMutex::CrossProcessMutex(const char*)
|
CrossProcessMutex::CrossProcessMutex(const char*)
|
||||||
: mSharedBuffer(nullptr)
|
: mSharedBuffer(nullptr)
|
||||||
, mMutex(nullptr)
|
, mMutex(nullptr)
|
||||||
|
@ -43,20 +61,7 @@ CrossProcessMutex::CrossProcessMutex(const char*)
|
||||||
mCount = &(data->mCount);
|
mCount = &(data->mCount);
|
||||||
|
|
||||||
*mCount = 1;
|
*mCount = 1;
|
||||||
|
InitMutex(mMutex);
|
||||||
pthread_mutexattr_t mutexAttributes;
|
|
||||||
pthread_mutexattr_init(&mutexAttributes);
|
|
||||||
// Make the mutex reentrant so it behaves the same as a win32 mutex
|
|
||||||
if (pthread_mutexattr_settype(&mutexAttributes, PTHREAD_MUTEX_RECURSIVE)) {
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
if (pthread_mutexattr_setpshared(&mutexAttributes, PTHREAD_PROCESS_SHARED)) {
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pthread_mutex_init(mMutex, &mutexAttributes)) {
|
|
||||||
MOZ_CRASH();
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_COUNT_CTOR(CrossProcessMutex);
|
MOZ_COUNT_CTOR(CrossProcessMutex);
|
||||||
}
|
}
|
||||||
|
@ -84,7 +89,13 @@ CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle aHandle)
|
||||||
|
|
||||||
mMutex = &(data->mMutex);
|
mMutex = &(data->mMutex);
|
||||||
mCount = &(data->mCount);
|
mCount = &(data->mCount);
|
||||||
(*mCount)++;
|
int32_t count = (*mCount)++;
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
// The other side has already let go of their CrossProcessMutex, so now
|
||||||
|
// mMutex is garbage. We need to re-initialize it.
|
||||||
|
InitMutex(mMutex);
|
||||||
|
}
|
||||||
|
|
||||||
MOZ_COUNT_CTOR(CrossProcessMutex);
|
MOZ_COUNT_CTOR(CrossProcessMutex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,3 +34,6 @@ MOCHITEST_MANIFESTS += [
|
||||||
'reftests/fonts/mochitest.ini',
|
'reftests/fonts/mochitest.ini',
|
||||||
'reftests/fonts/mplus/mochitest.ini',
|
'reftests/fonts/mplus/mochitest.ini',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REFTEST_MANIFESTS += ['reftests/reftest.list']
|
||||||
|
CRASHTEST_MANIFESTS += ['../testing/crashtest/crashtests.list']
|
||||||
|
|
|
@ -3,77 +3,27 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
import sys, os.path, re
|
import os
|
||||||
|
import sys
|
||||||
commentRE = re.compile(r"\s+#")
|
from reftest import ReftestManifest
|
||||||
conditionsRE = re.compile(r"^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|test-pref|ref-pref|fuzzy)")
|
|
||||||
httpRE = re.compile(r"HTTP\((\.\.(\/\.\.)*)\)")
|
|
||||||
protocolRE = re.compile(r"^\w+:")
|
|
||||||
|
|
||||||
def parseManifest(manifest, dirs):
|
|
||||||
"""Parse the reftest manifest |manifest|, adding all directories containing
|
|
||||||
tests (and the dirs containing the manifests themselves) to the set |dirs|."""
|
|
||||||
manifestdir = os.path.dirname(os.path.abspath(manifest))
|
|
||||||
dirs.add(manifestdir)
|
|
||||||
f = file(manifest)
|
|
||||||
urlprefix = ''
|
|
||||||
for line in f:
|
|
||||||
if line[0] == '#':
|
|
||||||
continue # entire line was a comment
|
|
||||||
m = commentRE.search(line)
|
|
||||||
if m:
|
|
||||||
line = line[:m.start()]
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
items = line.split()
|
|
||||||
while conditionsRE.match(items[0]):
|
|
||||||
del items[0]
|
|
||||||
if items[0] == "HTTP":
|
|
||||||
del items[0]
|
|
||||||
m = httpRE.match(items[0])
|
|
||||||
if m:
|
|
||||||
# need to package the dir referenced here
|
|
||||||
d = os.path.normpath(os.path.join(manifestdir, m.group(1)))
|
|
||||||
dirs.add(d)
|
|
||||||
del items[0]
|
|
||||||
|
|
||||||
if items[0] == "url-prefix":
|
|
||||||
urlprefix = items[1]
|
|
||||||
continue
|
|
||||||
elif items[0] == "default-preferences":
|
|
||||||
continue
|
|
||||||
elif items[0] == "include":
|
|
||||||
parseManifest(os.path.join(manifestdir, items[1]), dirs)
|
|
||||||
continue
|
|
||||||
elif items[0] == "load" or items[0] == "script":
|
|
||||||
testURLs = [items[1]]
|
|
||||||
elif items[0] == "==" or items[0] == "!=":
|
|
||||||
testURLs = items[1:3]
|
|
||||||
for u in testURLs:
|
|
||||||
m = protocolRE.match(u)
|
|
||||||
if m:
|
|
||||||
# can't very well package about: or data: URIs
|
|
||||||
continue
|
|
||||||
d = os.path.dirname(os.path.normpath(os.path.join(manifestdir, urlprefix + u)))
|
|
||||||
dirs.add(d)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def printTestDirs(topsrcdir, topmanifests):
|
def printTestDirs(topsrcdir, topmanifests):
|
||||||
"""Parse |topmanifests| and print a list of directories containing the tests
|
"""Parse |topmanifests| and print a list of directories containing the tests
|
||||||
within (and the manifests including those tests), relative to |topsrcdir|."""
|
within (and the manifests including those tests), relative to |topsrcdir|.
|
||||||
topsrcdir = os.path.abspath(topsrcdir)
|
"""
|
||||||
dirs = set()
|
topsrcdir = os.path.abspath(topsrcdir)
|
||||||
for manifest in topmanifests:
|
dirs = set()
|
||||||
parseManifest(manifest, dirs)
|
for path in topmanifests:
|
||||||
for dir in sorted(dirs):
|
m = ReftestManifest()
|
||||||
d = dir[len(topsrcdir):].replace('\\','/')
|
m.load(path)
|
||||||
if d[0] == '/':
|
dirs |= m.dirs
|
||||||
d = d[1:]
|
|
||||||
print d
|
for d in sorted(dirs):
|
||||||
|
d = d[len(topsrcdir):].replace('\\', '/').lstrip('/')
|
||||||
|
print(d)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print >>sys.stderr, "Usage: %s topsrcdir reftest.list [reftest.list]*" % sys.argv[0]
|
print >>sys.stderr, "Usage: %s topsrcdir reftest.list [reftest.list]*" % sys.argv[0]
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
printTestDirs(sys.argv[1], sys.argv[2:])
|
printTestDirs(sys.argv[1], sys.argv[2:])
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
RE_COMMENT = re.compile(r'\s+#')
|
||||||
|
RE_HTTP = re.compile(r'HTTP\((\.\.(\/\.\.)*)\)')
|
||||||
|
RE_PROTOCOL = re.compile(r'^\w+:')
|
||||||
|
FAILURE_TYPES = (
|
||||||
|
'fails',
|
||||||
|
'fails-if',
|
||||||
|
'needs-focus',
|
||||||
|
'random',
|
||||||
|
'random-if',
|
||||||
|
'silentfail',
|
||||||
|
'silentfail-if',
|
||||||
|
'skip',
|
||||||
|
'skip-if',
|
||||||
|
'slow',
|
||||||
|
'slow-if',
|
||||||
|
'fuzzy',
|
||||||
|
'fuzzy-if',
|
||||||
|
'require-or',
|
||||||
|
'asserts',
|
||||||
|
'asserts-if',
|
||||||
|
)
|
||||||
|
PREF_ITEMS = (
|
||||||
|
'pref',
|
||||||
|
'test-pref',
|
||||||
|
'ref-pref',
|
||||||
|
)
|
||||||
|
|
||||||
|
class ReftestManifest(object):
|
||||||
|
"""Represents a parsed reftest manifest.
|
||||||
|
|
||||||
|
We currently only capture file information because that is the only thing
|
||||||
|
tools require.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.path = None
|
||||||
|
self.dirs = set()
|
||||||
|
self.files = set()
|
||||||
|
self.manifests = set()
|
||||||
|
|
||||||
|
def load(self, path):
|
||||||
|
"""Parse a reftest manifest file."""
|
||||||
|
normalized = os.path.normpath(os.path.abspath(path))
|
||||||
|
self.manifests.add(normalized)
|
||||||
|
if not self.path:
|
||||||
|
self.path = normalized
|
||||||
|
|
||||||
|
mdir = os.path.dirname(normalized)
|
||||||
|
self.dirs.add(mdir)
|
||||||
|
|
||||||
|
with open(path, 'r') as fh:
|
||||||
|
urlprefix = ''
|
||||||
|
for line in fh:
|
||||||
|
line = line.decode('utf-8')
|
||||||
|
|
||||||
|
# Entire line is a comment.
|
||||||
|
if line.startswith('#'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Comments can begin mid line. Strip them.
|
||||||
|
m = RE_COMMENT.search(line)
|
||||||
|
if m:
|
||||||
|
line = line[:m.start()]
|
||||||
|
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
items = line.split()
|
||||||
|
tests = []
|
||||||
|
|
||||||
|
for i in range(len(items)):
|
||||||
|
item = items[i]
|
||||||
|
|
||||||
|
if item.startswith(FAILURE_TYPES):
|
||||||
|
continue
|
||||||
|
if item.startswith(PREF_ITEMS):
|
||||||
|
continue
|
||||||
|
if item == 'HTTP':
|
||||||
|
continue
|
||||||
|
|
||||||
|
m = RE_HTTP.match(item)
|
||||||
|
if m:
|
||||||
|
# Need to package the referenced directory.
|
||||||
|
self.dirs.add(os.path.normpath(os.path.join(
|
||||||
|
mdir, m.group(1))))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item == 'url-prefix':
|
||||||
|
urlprefix = items[i+1]
|
||||||
|
break
|
||||||
|
|
||||||
|
if item == 'default-preferences':
|
||||||
|
break
|
||||||
|
|
||||||
|
if item == 'include':
|
||||||
|
self.load(os.path.join(mdir, items[i+1]))
|
||||||
|
break
|
||||||
|
|
||||||
|
if item == 'load' or item == 'script':
|
||||||
|
tests.append(items[i+1])
|
||||||
|
break
|
||||||
|
|
||||||
|
if item == '==' or item == '!=':
|
||||||
|
tests.extend(items[i+1:i+3])
|
||||||
|
break
|
||||||
|
|
||||||
|
for f in tests:
|
||||||
|
# We can't package about: or data: URIs.
|
||||||
|
# Discarding data isn't correct for a parser. But retaining
|
||||||
|
# all data isn't currently a requirement.
|
||||||
|
if RE_PROTOCOL.match(f):
|
||||||
|
continue
|
||||||
|
|
||||||
|
test = os.path.normpath(os.path.join(mdir, urlprefix + f))
|
||||||
|
self.files.add(test)
|
||||||
|
self.dirs.add(os.path.dirname(test))
|
|
@ -121,7 +121,7 @@ pref("browser.sessionhistory.max_entries", 50);
|
||||||
pref("browser.sessionstore.resume_session_once", false);
|
pref("browser.sessionstore.resume_session_once", false);
|
||||||
pref("browser.sessionstore.resume_from_crash", true);
|
pref("browser.sessionstore.resume_from_crash", true);
|
||||||
pref("browser.sessionstore.interval", 10000); // milliseconds
|
pref("browser.sessionstore.interval", 10000); // milliseconds
|
||||||
pref("browser.sessionstore.max_tabs_undo", 1);
|
pref("browser.sessionstore.max_tabs_undo", 5);
|
||||||
pref("browser.sessionstore.max_resumed_crashes", 1);
|
pref("browser.sessionstore.max_resumed_crashes", 1);
|
||||||
pref("browser.sessionstore.recent_crashes", 0);
|
pref("browser.sessionstore.recent_crashes", 0);
|
||||||
|
|
||||||
|
|
|
@ -1130,6 +1130,10 @@ abstract public class BrowserApp extends GeckoApp
|
||||||
// Do exactly the same thing as if you tapped 'Sync' in Settings.
|
// Do exactly the same thing as if you tapped 'Sync' in Settings.
|
||||||
final Intent intent = new Intent(getContext(), FxAccountGetStartedActivity.class);
|
final Intent intent = new Intent(getContext(), FxAccountGetStartedActivity.class);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
final NativeJSObject extras = message.optObject("extras", null);
|
||||||
|
if (extras != null) {
|
||||||
|
intent.putExtra("extras", extras.toString());
|
||||||
|
}
|
||||||
getContext().startActivity(intent);
|
getContext().startActivity(intent);
|
||||||
|
|
||||||
} else if ("CharEncoding:Data".equals(event)) {
|
} else if ("CharEncoding:Data".equals(event)) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ public class FxAccountConstants {
|
||||||
public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
|
public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
|
||||||
public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
|
public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
|
||||||
|
|
||||||
|
public static final String STAGE_AUTH_SERVER_ENDPOINT = "https://api-accounts.stage.mozaws.net/v1";
|
||||||
public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
|
public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
|
||||||
|
|
||||||
// For extra debugging. Not final so it can be changed from Fennec, or from
|
// For extra debugging. Not final so it can be changed from Fennec, or from
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
import org.mozilla.gecko.fxa.login.Engaged;
|
import org.mozilla.gecko.fxa.login.Engaged;
|
||||||
import org.mozilla.gecko.fxa.login.State;
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.ProgressDisplay;
|
import org.mozilla.gecko.fxa.tasks.FxAccountSetupTask.ProgressDisplay;
|
||||||
|
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||||
import org.mozilla.gecko.sync.setup.Constants;
|
import org.mozilla.gecko.sync.setup.Constants;
|
||||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||||
|
@ -32,6 +33,7 @@ import android.accounts.AccountManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.text.method.PasswordTransformationMethod;
|
import android.text.method.PasswordTransformationMethod;
|
||||||
|
@ -50,6 +52,16 @@ import android.widget.TextView;
|
||||||
import android.widget.TextView.OnEditorActionListener;
|
import android.widget.TextView.OnEditorActionListener;
|
||||||
|
|
||||||
abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractActivity implements ProgressDisplay {
|
abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractActivity implements ProgressDisplay {
|
||||||
|
public static final String EXTRA_EMAIL = "email";
|
||||||
|
public static final String EXTRA_PASSWORD = "password";
|
||||||
|
public static final String EXTRA_PASSWORD_SHOWN = "password_shown";
|
||||||
|
public static final String EXTRA_YEAR = "year";
|
||||||
|
public static final String EXTRA_EXTRAS = "extras";
|
||||||
|
|
||||||
|
public static final String JSON_KEY_AUTH = "auth";
|
||||||
|
public static final String JSON_KEY_SERVICES = "services";
|
||||||
|
public static final String JSON_KEY_SYNC = "sync";
|
||||||
|
|
||||||
public FxAccountAbstractSetupActivity() {
|
public FxAccountAbstractSetupActivity() {
|
||||||
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST | CANNOT_RESUME_WHEN_LOCKED_OUT);
|
super(CANNOT_RESUME_WHEN_ACCOUNTS_EXIST | CANNOT_RESUME_WHEN_LOCKED_OUT);
|
||||||
}
|
}
|
||||||
|
@ -60,6 +72,10 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||||
|
|
||||||
private static final String LOG_TAG = FxAccountAbstractSetupActivity.class.getSimpleName();
|
private static final String LOG_TAG = FxAccountAbstractSetupActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
// By default, any custom server configuration is only shown when the account
|
||||||
|
// is configured to use a custom server.
|
||||||
|
private static boolean ALWAYS_SHOW_CUSTOM_SERVER_LAYOUT = false;
|
||||||
|
|
||||||
protected int minimumPasswordLength = 8;
|
protected int minimumPasswordLength = 8;
|
||||||
|
|
||||||
protected AutoCompleteTextView emailEdit;
|
protected AutoCompleteTextView emailEdit;
|
||||||
|
@ -69,33 +85,47 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||||
protected Button button;
|
protected Button button;
|
||||||
protected ProgressBar progressBar;
|
protected ProgressBar progressBar;
|
||||||
|
|
||||||
|
private String authServerEndpoint;
|
||||||
|
private String syncServerEndpoint;
|
||||||
|
|
||||||
|
protected String getAuthServerEndpoint() {
|
||||||
|
return authServerEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getTokenServerEndpoint() {
|
||||||
|
return syncServerEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
protected void createShowPasswordButton() {
|
protected void createShowPasswordButton() {
|
||||||
showPasswordButton.setOnClickListener(new OnClickListener() {
|
showPasswordButton.setOnClickListener(new OnClickListener() {
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
boolean isShown = passwordEdit.getTransformationMethod() instanceof SingleLineTransformationMethod;
|
boolean isShown = passwordEdit.getTransformationMethod() instanceof SingleLineTransformationMethod;
|
||||||
|
setPasswordButtonShown(!isShown);
|
||||||
// Changing input type loses position in edit text; let's try to maintain it.
|
|
||||||
int start = passwordEdit.getSelectionStart();
|
|
||||||
int stop = passwordEdit.getSelectionEnd();
|
|
||||||
|
|
||||||
if (isShown) {
|
|
||||||
passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
|
||||||
showPasswordButton.setText(R.string.fxaccount_password_show);
|
|
||||||
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
|
|
||||||
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_show_textcolor));
|
|
||||||
} else {
|
|
||||||
passwordEdit.setTransformationMethod(SingleLineTransformationMethod.getInstance());
|
|
||||||
showPasswordButton.setText(R.string.fxaccount_password_hide);
|
|
||||||
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
|
|
||||||
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_hide_textcolor));
|
|
||||||
}
|
|
||||||
passwordEdit.setSelection(start, stop);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected void setPasswordButtonShown(boolean shouldShow) {
|
||||||
|
// Changing input type loses position in edit text; let's try to maintain it.
|
||||||
|
int start = passwordEdit.getSelectionStart();
|
||||||
|
int stop = passwordEdit.getSelectionEnd();
|
||||||
|
|
||||||
|
if (!shouldShow) {
|
||||||
|
passwordEdit.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||||
|
showPasswordButton.setText(R.string.fxaccount_password_show);
|
||||||
|
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_show_background));
|
||||||
|
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_show_textcolor));
|
||||||
|
} else {
|
||||||
|
passwordEdit.setTransformationMethod(SingleLineTransformationMethod.getInstance());
|
||||||
|
showPasswordButton.setText(R.string.fxaccount_password_hide);
|
||||||
|
showPasswordButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.fxaccount_password_button_hide_background));
|
||||||
|
showPasswordButton.setTextColor(getResources().getColor(R.color.fxaccount_password_hide_textcolor));
|
||||||
|
}
|
||||||
|
passwordEdit.setSelection(start, stop);
|
||||||
|
}
|
||||||
|
|
||||||
protected void linkifyPolicy() {
|
protected void linkifyPolicy() {
|
||||||
TextView policyView = (TextView) ensureFindViewById(null, R.id.policy, "policy links");
|
TextView policyView = (TextView) ensureFindViewById(null, R.id.policy, "policy links");
|
||||||
final String linkTerms = getString(R.string.fxaccount_link_tos);
|
final String linkTerms = getString(R.string.fxaccount_link_tos);
|
||||||
|
@ -262,7 +292,7 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||||
AndroidFxAccount fxAccount;
|
AndroidFxAccount fxAccount;
|
||||||
try {
|
try {
|
||||||
final String profile = Constants.DEFAULT_PROFILE;
|
final String profile = Constants.DEFAULT_PROFILE;
|
||||||
final String tokenServerURI = FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT;
|
final String tokenServerURI = getTokenServerEndpoint();
|
||||||
// It is crucial that we use the email address provided by the server
|
// It is crucial that we use the email address provided by the server
|
||||||
// (rather than whatever the user entered), because the user's keys are
|
// (rather than whatever the user entered), because the user's keys are
|
||||||
// wrapped and salted with the initial email they provided to
|
// wrapped and salted with the initial email they provided to
|
||||||
|
@ -356,6 +386,31 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||||
emailEdit.setAdapter(adapter);
|
emailEdit.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateFromIntentExtras() {
|
||||||
|
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
|
||||||
|
if (getIntent() != null && getIntent().getExtras() != null) {
|
||||||
|
Bundle bundle = getIntent().getExtras();
|
||||||
|
emailEdit.setText(bundle.getString(EXTRA_EMAIL));
|
||||||
|
passwordEdit.setText(bundle.getString(EXTRA_PASSWORD));
|
||||||
|
setPasswordButtonShown(bundle.getBoolean(EXTRA_PASSWORD_SHOWN, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This sets defaults as well as extracting from extras, so it's not conditional.
|
||||||
|
updateServersFromIntentExtras(getIntent());
|
||||||
|
|
||||||
|
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
|
||||||
|
FxAccountConstants.pii(LOG_TAG, "Using auth server: " + authServerEndpoint);
|
||||||
|
FxAccountConstants.pii(LOG_TAG, "Using sync server: " + syncServerEndpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCustomServerView();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
@ -370,4 +425,110 @@ abstract public class FxAccountAbstractSetupActivity extends FxAccountAbstractAc
|
||||||
};
|
};
|
||||||
task.execute();
|
task.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Bundle makeExtrasBundle(String email, String password) {
|
||||||
|
final Bundle bundle = new Bundle();
|
||||||
|
|
||||||
|
// Pass through any extras that we were started with.
|
||||||
|
if (getIntent() != null && getIntent().getExtras() != null) {
|
||||||
|
bundle.putAll(getIntent().getExtras());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite with current settings.
|
||||||
|
if (email == null) {
|
||||||
|
email = emailEdit.getText().toString();
|
||||||
|
}
|
||||||
|
if (password == null) {
|
||||||
|
password = passwordEdit.getText().toString();
|
||||||
|
}
|
||||||
|
bundle.putString(EXTRA_EMAIL, email);
|
||||||
|
bundle.putString(EXTRA_PASSWORD, password);
|
||||||
|
|
||||||
|
boolean isPasswordShown = passwordEdit.getTransformationMethod() instanceof SingleLineTransformationMethod;
|
||||||
|
bundle.putBoolean(EXTRA_PASSWORD_SHOWN, isPasswordShown);
|
||||||
|
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void startActivityInstead(Class<?> cls, int requestCode, Bundle extras) {
|
||||||
|
Intent intent = new Intent(this, cls);
|
||||||
|
if (extras != null) {
|
||||||
|
intent.putExtras(extras);
|
||||||
|
}
|
||||||
|
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
||||||
|
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
|
startActivityForResult(intent, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateServersFromIntentExtras(Intent intent) {
|
||||||
|
// Start with defaults.
|
||||||
|
this.authServerEndpoint = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
|
||||||
|
this.syncServerEndpoint = FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT;
|
||||||
|
|
||||||
|
if (intent == null) {
|
||||||
|
Logger.warn(LOG_TAG, "Intent is null; ignoring and using default servers.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String extrasString = intent.getStringExtra(EXTRA_EXTRAS);
|
||||||
|
|
||||||
|
if (extrasString == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ExtendedJSONObject extras;
|
||||||
|
final ExtendedJSONObject services;
|
||||||
|
try {
|
||||||
|
extras = new ExtendedJSONObject(extrasString);
|
||||||
|
services = extras.getObject(JSON_KEY_SERVICES);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.warn(LOG_TAG, "Got exception parsing extras; ignoring and using default servers.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String authServer = extras.getString(JSON_KEY_AUTH);
|
||||||
|
String syncServer = services == null ? null : services.getString(JSON_KEY_SYNC);
|
||||||
|
|
||||||
|
if (authServer != null) {
|
||||||
|
this.authServerEndpoint = authServer;
|
||||||
|
}
|
||||||
|
if (syncServer != null) {
|
||||||
|
this.syncServerEndpoint = syncServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT.equals(syncServerEndpoint) &&
|
||||||
|
!FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT.equals(authServerEndpoint)) {
|
||||||
|
// We really don't want to hard-code assumptions about server
|
||||||
|
// configurations into client code in such a way that if and when the
|
||||||
|
// situation is relaxed, the client code stops valid usage. Instead, we
|
||||||
|
// warn. This configuration should present itself as an auth exception at
|
||||||
|
// Sync time.
|
||||||
|
Logger.warn(LOG_TAG, "Mozilla's Sync token servers only works with Mozilla's auth servers. Sync will likely be mis-configured.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateCustomServerView() {
|
||||||
|
final boolean shouldShow =
|
||||||
|
ALWAYS_SHOW_CUSTOM_SERVER_LAYOUT ||
|
||||||
|
!FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT.equals(authServerEndpoint) ||
|
||||||
|
!FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT.equals(syncServerEndpoint);
|
||||||
|
|
||||||
|
if (!shouldShow) {
|
||||||
|
setCustomServerViewVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final TextView authServerView = (TextView) ensureFindViewById(null, R.id.account_server_summary, "account server");
|
||||||
|
final TextView syncServerView = (TextView) ensureFindViewById(null, R.id.sync_server_summary, "Sync server");
|
||||||
|
authServerView.setText(authServerEndpoint);
|
||||||
|
syncServerView.setText(syncServerEndpoint);
|
||||||
|
|
||||||
|
setCustomServerViewVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setCustomServerViewVisibility(int visibility) {
|
||||||
|
ensureFindViewById(null, R.id.account_server_layout, "account server layout").setVisibility(visibility);
|
||||||
|
ensureFindViewById(null, R.id.sync_server_layout, "sync server layout").setVisibility(visibility);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,32 +89,29 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
||||||
signInInsteadLink.setOnClickListener(new OnClickListener() {
|
signInInsteadLink.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
final String email = emailEdit.getText().toString();
|
final Bundle extras = makeExtrasBundle(null, null);
|
||||||
final String password = passwordEdit.getText().toString();
|
startActivityInstead(FxAccountSignInActivity.class, CHILD_REQUEST_CODE, extras);
|
||||||
doSigninInstead(email, password);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
|
updateFromIntentExtras();
|
||||||
if (getIntent() != null && getIntent().getExtras() != null) {
|
|
||||||
Bundle bundle = getIntent().getExtras();
|
|
||||||
emailEdit.setText(bundle.getString("email"));
|
|
||||||
passwordEdit.setText(bundle.getString("password"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doSigninInstead(final String email, final String password) {
|
@Override
|
||||||
Intent intent = new Intent(this, FxAccountSignInActivity.class);
|
protected Bundle makeExtrasBundle(String email, String password) {
|
||||||
if (email != null) {
|
final Bundle extras = super.makeExtrasBundle(email, password);
|
||||||
intent.putExtra("email", email);
|
final String year = yearEdit.getText().toString();
|
||||||
|
extras.putString(EXTRA_YEAR, year);
|
||||||
|
return extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateFromIntentExtras() {
|
||||||
|
super.updateFromIntentExtras();
|
||||||
|
|
||||||
|
if (getIntent() != null) {
|
||||||
|
yearEdit.setText(getIntent().getStringExtra(EXTRA_YEAR));
|
||||||
}
|
}
|
||||||
if (password != null) {
|
|
||||||
intent.putExtra("password", password);
|
|
||||||
}
|
|
||||||
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
|
||||||
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
|
||||||
startActivityForResult(intent, CHILD_REQUEST_CODE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,7 +139,9 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
||||||
email = emailEdit.getText().toString();
|
email = emailEdit.getText().toString();
|
||||||
}
|
}
|
||||||
final String password = passwordEdit.getText().toString();
|
final String password = passwordEdit.getText().toString();
|
||||||
doSigninInstead(email, password);
|
|
||||||
|
final Bundle extras = makeExtrasBundle(email, password);
|
||||||
|
startActivityInstead(FxAccountSignInActivity.class, CHILD_REQUEST_CODE, extras);
|
||||||
}
|
}
|
||||||
}, clickableStart, clickableEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
}, clickableStart, clickableEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
remoteErrorTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
remoteErrorTextView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
@ -205,7 +204,7 @@ public class FxAccountCreateAccountActivity extends FxAccountAbstractSetupActivi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createAccount(String email, String password, Map<String, Boolean> engines) {
|
public void createAccount(String email, String password, Map<String, Boolean> engines) {
|
||||||
String serverURI = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
|
String serverURI = getAuthServerEndpoint();
|
||||||
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
|
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
|
||||||
// This delegate creates a new Android account on success, opens the
|
// This delegate creates a new Android account on success, opens the
|
||||||
// appropriate "success!" activity, and finishes this activity.
|
// appropriate "success!" activity, and finishes this activity.
|
||||||
|
|
|
@ -50,15 +50,26 @@ public class FxAccountGetStartedActivity extends AccountAuthenticatorActivity {
|
||||||
button.setOnClickListener(new OnClickListener() {
|
button.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(FxAccountGetStartedActivity.this, FxAccountCreateAccountActivity.class);
|
Bundle extras = null; // startFlow accepts null.
|
||||||
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
if (getIntent() != null) {
|
||||||
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
extras = getIntent().getExtras();
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
}
|
||||||
startActivityForResult(intent, CHILD_REQUEST_CODE);
|
startFlow(extras);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void startFlow(Bundle extras) {
|
||||||
|
final Intent intent = new Intent(this, FxAccountCreateAccountActivity.class);
|
||||||
|
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
||||||
|
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
|
if (extras != null) {
|
||||||
|
intent.putExtras(extras);
|
||||||
|
}
|
||||||
|
startActivityForResult(intent, CHILD_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
@ -79,6 +90,20 @@ public class FxAccountGetStartedActivity extends AccountAuthenticatorActivity {
|
||||||
this.startActivity(intent);
|
this.startActivity(intent);
|
||||||
this.finish();
|
this.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we've been launched with extras (namely custom server URLs), continue
|
||||||
|
// past go and collect 200 dollars. If we ever get back here (for example,
|
||||||
|
// if the user hits the back button), forget that we had extras entirely, so
|
||||||
|
// that we don't enter a loop.
|
||||||
|
Bundle extras = null;
|
||||||
|
if (getIntent() != null) {
|
||||||
|
extras = getIntent().getExtras();
|
||||||
|
}
|
||||||
|
if (extras != null && extras.containsKey(FxAccountAbstractSetupActivity.EXTRA_EXTRAS)) {
|
||||||
|
getIntent().replaceExtras(Bundle.EMPTY);
|
||||||
|
startFlow((Bundle) extras.clone());
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,7 +15,6 @@ import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
|
import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
|
||||||
import org.mozilla.gecko.background.fxa.PasswordStretcher;
|
import org.mozilla.gecko.background.fxa.PasswordStretcher;
|
||||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
|
||||||
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
|
import org.mozilla.gecko.fxa.tasks.FxAccountSignInTask;
|
||||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||||
|
|
||||||
|
@ -65,22 +64,12 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
|
||||||
createAccountInsteadLink.setOnClickListener(new OnClickListener() {
|
createAccountInsteadLink.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(FxAccountSignInActivity.this, FxAccountCreateAccountActivity.class);
|
final Bundle extras = makeExtrasBundle(null, null);
|
||||||
intent.putExtra("email", emailEdit.getText().toString());
|
startActivityInstead(FxAccountCreateAccountActivity.class, CHILD_REQUEST_CODE, extras);
|
||||||
intent.putExtra("password", passwordEdit.getText().toString());
|
|
||||||
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
|
||||||
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
|
||||||
startActivityForResult(intent, CHILD_REQUEST_CODE);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Only set email/password in onCreate; we don't want to overwrite edited values onResume.
|
updateFromIntentExtras();
|
||||||
if (getIntent() != null && getIntent().getExtras() != null) {
|
|
||||||
Bundle bundle = getIntent().getExtras();
|
|
||||||
emailEdit.setText(bundle.getString("email"));
|
|
||||||
passwordEdit.setText(bundle.getString("password"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TextView view = (TextView) findViewById(R.id.forgot_password_link);
|
TextView view = (TextView) findViewById(R.id.forgot_password_link);
|
||||||
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
|
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
|
||||||
|
@ -102,7 +91,7 @@ public class FxAccountSignInActivity extends FxAccountAbstractSetupActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void signIn(String email, String password) {
|
public void signIn(String email, String password) {
|
||||||
String serverURI = FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT;
|
String serverURI = getAuthServerEndpoint();
|
||||||
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
|
PasswordStretcher passwordStretcher = makePasswordStretcher(password);
|
||||||
// This delegate creates a new Android account on success, opens the
|
// This delegate creates a new Android account on success, opens the
|
||||||
// appropriate "success!" activity, and finishes this activity.
|
// appropriate "success!" activity, and finishes this activity.
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.mozilla.gecko.fxa.login.Married;
|
||||||
import org.mozilla.gecko.fxa.login.State;
|
import org.mozilla.gecko.fxa.login.State;
|
||||||
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
|
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
|
||||||
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
|
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
|
||||||
|
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||||
|
|
||||||
|
@ -56,7 +57,17 @@ public class FxAccountStatusFragment
|
||||||
// collection.
|
// collection.
|
||||||
private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
|
private static final long DELAY_IN_MILLISECONDS_BEFORE_REQUESTING_SYNC = 5 * 1000;
|
||||||
|
|
||||||
|
// By default, the auth/account server preference is only shown when the
|
||||||
|
// account is configured to use a custom server. In debug mode, this is set.
|
||||||
|
private static boolean ALWAYS_SHOW_AUTH_SERVER = false;
|
||||||
|
|
||||||
|
// By default, the Sync server preference is only shown when the account is
|
||||||
|
// configured to use a custom Sync server. In debug mode, this is set.
|
||||||
|
private static boolean ALWAYS_SHOW_SYNC_SERVER = false;
|
||||||
|
|
||||||
|
protected PreferenceCategory accountCategory;
|
||||||
protected Preference emailPreference;
|
protected Preference emailPreference;
|
||||||
|
protected Preference authServerPreference;
|
||||||
|
|
||||||
protected Preference needsPasswordPreference;
|
protected Preference needsPasswordPreference;
|
||||||
protected Preference needsUpgradePreference;
|
protected Preference needsUpgradePreference;
|
||||||
|
@ -72,6 +83,7 @@ public class FxAccountStatusFragment
|
||||||
protected CheckBoxPreference passwordsPreference;
|
protected CheckBoxPreference passwordsPreference;
|
||||||
|
|
||||||
protected EditTextPreference deviceNamePreference;
|
protected EditTextPreference deviceNamePreference;
|
||||||
|
protected Preference syncServerPreference;
|
||||||
|
|
||||||
protected volatile AndroidFxAccount fxAccount;
|
protected volatile AndroidFxAccount fxAccount;
|
||||||
// The contract is: when fxAccount is non-null, then clientsDataDelegate is
|
// The contract is: when fxAccount is non-null, then clientsDataDelegate is
|
||||||
|
@ -105,7 +117,9 @@ public class FxAccountStatusFragment
|
||||||
protected void addPreferences() {
|
protected void addPreferences() {
|
||||||
addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
|
addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
|
||||||
|
|
||||||
|
accountCategory = (PreferenceCategory) ensureFindPreference("signed_in_as_category");
|
||||||
emailPreference = ensureFindPreference("email");
|
emailPreference = ensureFindPreference("email");
|
||||||
|
authServerPreference = ensureFindPreference("auth_server");
|
||||||
|
|
||||||
needsPasswordPreference = ensureFindPreference("needs_credentials");
|
needsPasswordPreference = ensureFindPreference("needs_credentials");
|
||||||
needsUpgradePreference = ensureFindPreference("needs_upgrade");
|
needsUpgradePreference = ensureFindPreference("needs_upgrade");
|
||||||
|
@ -124,6 +138,8 @@ public class FxAccountStatusFragment
|
||||||
removeDebugButtons();
|
removeDebugButtons();
|
||||||
} else {
|
} else {
|
||||||
connectDebugButtons();
|
connectDebugButtons();
|
||||||
|
ALWAYS_SHOW_AUTH_SERVER = true;
|
||||||
|
ALWAYS_SHOW_SYNC_SERVER = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
needsPasswordPreference.setOnPreferenceClickListener(this);
|
needsPasswordPreference.setOnPreferenceClickListener(this);
|
||||||
|
@ -137,6 +153,8 @@ public class FxAccountStatusFragment
|
||||||
|
|
||||||
deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
|
deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
|
||||||
deviceNamePreference.setOnPreferenceChangeListener(this);
|
deviceNamePreference.setOnPreferenceChangeListener(this);
|
||||||
|
|
||||||
|
syncServerPreference = ensureFindPreference("sync_server");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,6 +170,10 @@ public class FxAccountStatusFragment
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
if (preference == needsPasswordPreference) {
|
if (preference == needsPasswordPreference) {
|
||||||
Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
|
Intent intent = new Intent(getActivity(), FxAccountUpdateCredentialsActivity.class);
|
||||||
|
final Bundle extras = getExtrasForAccount();
|
||||||
|
if (extras != null) {
|
||||||
|
intent.putExtras(extras);
|
||||||
|
}
|
||||||
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
// Per http://stackoverflow.com/a/8992365, this triggers a known bug with
|
||||||
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
// the soft keyboard not being shown for the started activity. Why, Android, why?
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
|
@ -190,6 +212,17 @@ public class FxAccountStatusFragment
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Bundle getExtrasForAccount() {
|
||||||
|
final Bundle extras = new Bundle();
|
||||||
|
final ExtendedJSONObject o = new ExtendedJSONObject();
|
||||||
|
o.put(FxAccountAbstractSetupActivity.JSON_KEY_AUTH, fxAccount.getAccountServerURI());
|
||||||
|
final ExtendedJSONObject services = new ExtendedJSONObject();
|
||||||
|
services.put(FxAccountAbstractSetupActivity.JSON_KEY_SYNC, fxAccount.getTokenServerURI());
|
||||||
|
o.put(FxAccountAbstractSetupActivity.JSON_KEY_SERVICES, services);
|
||||||
|
extras.putString(FxAccountAbstractSetupActivity.EXTRA_EXTRAS, o.toJSONString());
|
||||||
|
return extras;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setCheckboxesEnabled(boolean enabled) {
|
protected void setCheckboxesEnabled(boolean enabled) {
|
||||||
bookmarksPreference.setEnabled(enabled);
|
bookmarksPreference.setEnabled(enabled);
|
||||||
historyPreference.setEnabled(enabled);
|
historyPreference.setEnabled(enabled);
|
||||||
|
@ -367,6 +400,8 @@ public class FxAccountStatusFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
emailPreference.setTitle(fxAccount.getEmail());
|
emailPreference.setTitle(fxAccount.getEmail());
|
||||||
|
updateAuthServerPreference();
|
||||||
|
updateSyncServerPreference();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// There are error states determined by Android, not the login state
|
// There are error states determined by Android, not the login state
|
||||||
|
@ -417,6 +452,38 @@ public class FxAccountStatusFragment
|
||||||
deviceNamePreference.setText(clientName);
|
deviceNamePreference.setText(clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateAuthServerPreference() {
|
||||||
|
final String authServer = fxAccount.getAccountServerURI();
|
||||||
|
final boolean shouldBeShown = ALWAYS_SHOW_AUTH_SERVER || !FxAccountConstants.DEFAULT_AUTH_SERVER_ENDPOINT.equals(authServer);
|
||||||
|
final boolean currentlyShown = null != findPreference(authServerPreference.getKey());
|
||||||
|
if (currentlyShown != shouldBeShown) {
|
||||||
|
if (shouldBeShown) {
|
||||||
|
accountCategory.addPreference(authServerPreference);
|
||||||
|
} else {
|
||||||
|
accountCategory.removePreference(authServerPreference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always set the summary, because on first run, the preference is visible,
|
||||||
|
// and the above block will be skipped if there is a custom value.
|
||||||
|
authServerPreference.setSummary(authServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateSyncServerPreference() {
|
||||||
|
final String syncServer = fxAccount.getTokenServerURI();
|
||||||
|
final boolean shouldBeShown = ALWAYS_SHOW_SYNC_SERVER || !FxAccountConstants.DEFAULT_TOKEN_SERVER_ENDPOINT.equals(syncServer);
|
||||||
|
final boolean currentlyShown = null != findPreference(syncServerPreference.getKey());
|
||||||
|
if (currentlyShown != shouldBeShown) {
|
||||||
|
if (shouldBeShown) {
|
||||||
|
syncCategory.addPreference(syncServerPreference);
|
||||||
|
} else {
|
||||||
|
syncCategory.removePreference(syncServerPreference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always set the summary, because on first run, the preference is visible,
|
||||||
|
// and the above block will be skipped if there is a custom value.
|
||||||
|
syncServerPreference.setSummary(syncServer);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query shared prefs for the current engine state, and update the UI
|
* Query shared prefs for the current engine state, and update the UI
|
||||||
* accordingly.
|
* accordingly.
|
||||||
|
|
|
@ -77,6 +77,8 @@ public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupAc
|
||||||
|
|
||||||
TextView view = (TextView) findViewById(R.id.forgot_password_link);
|
TextView view = (TextView) findViewById(R.id.forgot_password_link);
|
||||||
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
|
ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password);
|
||||||
|
|
||||||
|
updateFromIntentExtras();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -56,18 +56,18 @@ public class AccountPickler {
|
||||||
|
|
||||||
public static final long PICKLE_VERSION = 2;
|
public static final long PICKLE_VERSION = 2;
|
||||||
|
|
||||||
private static final String KEY_PICKLE_VERSION = "pickle_version";
|
public static final String KEY_PICKLE_VERSION = "pickle_version";
|
||||||
private static final String KEY_PICKLE_TIMESTAMP = "pickle_timestamp";
|
public static final String KEY_PICKLE_TIMESTAMP = "pickle_timestamp";
|
||||||
|
|
||||||
private static final String KEY_ACCOUNT_VERSION = "account_version";
|
public static final String KEY_ACCOUNT_VERSION = "account_version";
|
||||||
private static final String KEY_ACCOUNT_TYPE = "account_type";
|
public static final String KEY_ACCOUNT_TYPE = "account_type";
|
||||||
private static final String KEY_EMAIL = "email";
|
public static final String KEY_EMAIL = "email";
|
||||||
private static final String KEY_PROFILE = "profile";
|
public static final String KEY_PROFILE = "profile";
|
||||||
private static final String KEY_IDP_SERVER_URI = "idpServerURI";
|
public static final String KEY_IDP_SERVER_URI = "idpServerURI";
|
||||||
private static final String KEY_TOKEN_SERVER_URI = "tokenServerURI";
|
public static final String KEY_TOKEN_SERVER_URI = "tokenServerURI";
|
||||||
private static final String KEY_IS_SYNCING_ENABLED = "isSyncingEnabled";
|
public static final String KEY_IS_SYNCING_ENABLED = "isSyncingEnabled";
|
||||||
|
|
||||||
private static final String KEY_BUNDLE = "bundle";
|
public static final String KEY_BUNDLE = "bundle";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove Firefox account persisted to disk.
|
* Remove Firefox account persisted to disk.
|
||||||
|
@ -80,16 +80,10 @@ public class AccountPickler {
|
||||||
return context.deleteFile(filename);
|
return context.deleteFile(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static ExtendedJSONObject toJSON(final AndroidFxAccount account, final long now) {
|
||||||
* Persist Firefox account to disk as a JSON object.
|
|
||||||
*
|
|
||||||
* @param AndroidFxAccount the account to persist to disk
|
|
||||||
* @param filename name of file to persist to; must not contain path separators.
|
|
||||||
*/
|
|
||||||
public static void pickle(final AndroidFxAccount account, final String filename) {
|
|
||||||
final ExtendedJSONObject o = new ExtendedJSONObject();
|
final ExtendedJSONObject o = new ExtendedJSONObject();
|
||||||
o.put(KEY_PICKLE_VERSION, Long.valueOf(PICKLE_VERSION));
|
o.put(KEY_PICKLE_VERSION, Long.valueOf(PICKLE_VERSION));
|
||||||
o.put(KEY_PICKLE_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));
|
o.put(KEY_PICKLE_TIMESTAMP, Long.valueOf(now));
|
||||||
|
|
||||||
o.put(KEY_ACCOUNT_VERSION, AndroidFxAccount.CURRENT_ACCOUNT_VERSION);
|
o.put(KEY_ACCOUNT_VERSION, AndroidFxAccount.CURRENT_ACCOUNT_VERSION);
|
||||||
o.put(KEY_ACCOUNT_TYPE, FxAccountConstants.ACCOUNT_TYPE);
|
o.put(KEY_ACCOUNT_TYPE, FxAccountConstants.ACCOUNT_TYPE);
|
||||||
|
@ -104,10 +98,21 @@ public class AccountPickler {
|
||||||
final ExtendedJSONObject bundle = account.unbundle();
|
final ExtendedJSONObject bundle = account.unbundle();
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
Logger.warn(LOG_TAG, "Unable to obtain account bundle; aborting.");
|
Logger.warn(LOG_TAG, "Unable to obtain account bundle; aborting.");
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
o.put(KEY_BUNDLE, bundle);
|
o.put(KEY_BUNDLE, bundle);
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist Firefox account to disk as a JSON object.
|
||||||
|
*
|
||||||
|
* @param AndroidFxAccount the account to persist to disk
|
||||||
|
* @param filename name of file to persist to; must not contain path separators.
|
||||||
|
*/
|
||||||
|
public static void pickle(final AndroidFxAccount account, final String filename) {
|
||||||
|
final ExtendedJSONObject o = toJSON(account, System.currentTimeMillis());
|
||||||
writeToDisk(account.context, filename, o);
|
writeToDisk(account.context, filename, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -323,6 +323,9 @@ public class AndroidFxAccount {
|
||||||
if (email == null) {
|
if (email == null) {
|
||||||
throw new IllegalArgumentException("email must not be null");
|
throw new IllegalArgumentException("email must not be null");
|
||||||
}
|
}
|
||||||
|
if (profile == null) {
|
||||||
|
throw new IllegalArgumentException("profile must not be null");
|
||||||
|
}
|
||||||
if (idpServerURI == null) {
|
if (idpServerURI == null) {
|
||||||
throw new IllegalArgumentException("idpServerURI must not be null");
|
throw new IllegalArgumentException("idpServerURI must not be null");
|
||||||
}
|
}
|
||||||
|
@ -368,6 +371,15 @@ public class AndroidFxAccount {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to work around an intermittent issue described at
|
||||||
|
// http://stackoverflow.com/a/11698139. What happens is that tests that
|
||||||
|
// delete and re-create the same account frequently will find the account
|
||||||
|
// missing all or some of the userdata bundle, possibly due to an Android
|
||||||
|
// AccountManager caching bug.
|
||||||
|
for (String key : userdata.keySet()) {
|
||||||
|
accountManager.setUserData(account, key, userdata.getString(key));
|
||||||
|
}
|
||||||
|
|
||||||
AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
|
AndroidFxAccount fxAccount = new AndroidFxAccount(context, account);
|
||||||
|
|
||||||
if (!fromPickle) {
|
if (!fromPickle) {
|
||||||
|
|
|
@ -5,23 +5,81 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.home;
|
package org.mozilla.gecko.home;
|
||||||
|
|
||||||
import org.mozilla.gecko.R;
|
import java.util.Date;
|
||||||
import org.mozilla.gecko.widget.IconTabWidget;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
import org.mozilla.gecko.R;
|
||||||
|
import org.mozilla.gecko.Telemetry;
|
||||||
|
import org.mozilla.gecko.TelemetryContract;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract.Combined;
|
||||||
|
import org.mozilla.gecko.db.BrowserDB;
|
||||||
|
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||||
|
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
||||||
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||||
|
import android.support.v4.content.Loader;
|
||||||
|
import android.util.SparseArray;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewStub;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
public class HistoryPanel extends HomeFragment
|
/**
|
||||||
implements IconTabWidget.OnTabChangedListener {
|
* Fragment that displays recent history in a ListView.
|
||||||
|
*/
|
||||||
|
public class HistoryPanel extends HomeFragment {
|
||||||
// Logging tag name
|
// Logging tag name
|
||||||
private static final String LOGTAG = "GeckoHistoryPanel";
|
private static final String LOGTAG = "GeckoHistoryPanel";
|
||||||
private IconTabWidget mTabWidget;
|
|
||||||
private int mSelectedTab;
|
// Cursor loader ID for history query
|
||||||
private boolean initializeRecentPanel;
|
private static final int LOADER_ID_HISTORY = 0;
|
||||||
|
|
||||||
|
// Adapter for the list of recent history entries.
|
||||||
|
private HistoryAdapter mAdapter;
|
||||||
|
|
||||||
|
// The view shown by the fragment.
|
||||||
|
private HomeListView mList;
|
||||||
|
|
||||||
|
// The button view for clearing browsing history.
|
||||||
|
private View mClearHistoryButton;
|
||||||
|
|
||||||
|
// Reference to the View to display when there are no results.
|
||||||
|
private View mEmptyView;
|
||||||
|
|
||||||
|
// Callbacks used for the search and favicon cursor loaders
|
||||||
|
private CursorLoaderCallbacks mCursorLoaderCallbacks;
|
||||||
|
|
||||||
|
// On URL open listener
|
||||||
|
private OnUrlOpenListener mUrlOpenListener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity) {
|
||||||
|
super.onAttach(activity);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mUrlOpenListener = (OnUrlOpenListener) activity;
|
||||||
|
} catch (ClassCastException e) {
|
||||||
|
throw new ClassCastException(activity.toString()
|
||||||
|
+ " must implement HomePager.OnUrlOpenListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
mUrlOpenListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -30,75 +88,335 @@ public class HistoryPanel extends HomeFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
mList = (HomeListView) view.findViewById(R.id.list);
|
||||||
|
mList.setTag(HomePager.LIST_TAG_HISTORY);
|
||||||
|
|
||||||
mTabWidget = (IconTabWidget) view.findViewById(R.id.tab_icon_widget);
|
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
position -= mAdapter.getMostRecentSectionsCountBefore(position);
|
||||||
|
final Cursor c = mAdapter.getCursor(position);
|
||||||
|
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
|
||||||
|
|
||||||
mTabWidget.addTab(R.drawable.icon_most_recent, R.string.home_most_recent_title);
|
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
|
||||||
mTabWidget.addTab(R.drawable.icon_last_tabs, R.string.home_last_tabs_title);
|
|
||||||
|
|
||||||
mTabWidget.setTabSelectionListener(this);
|
// This item is a TwoLinePageRow, so we allow switch-to-tab.
|
||||||
mTabWidget.setCurrentTab(mSelectedTab);
|
mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
|
||||||
|
@Override
|
||||||
|
public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
|
||||||
|
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
|
||||||
|
info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
|
||||||
|
info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
|
||||||
|
info.display = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.DISPLAY));
|
||||||
|
info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
|
||||||
|
final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
|
||||||
|
if (cursor.isNull(bookmarkIdCol)) {
|
||||||
|
// If this is a combined cursor, we may get a history item without a
|
||||||
|
// bookmark, in which case the bookmarks ID column value will be null.
|
||||||
|
info.bookmarkId = -1;
|
||||||
|
} else {
|
||||||
|
info.bookmarkId = cursor.getInt(bookmarkIdCol);
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
registerForContextMenu(mList);
|
||||||
|
|
||||||
|
mClearHistoryButton = view.findViewById(R.id.clear_history_button);
|
||||||
|
mClearHistoryButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
final Context context = getActivity();
|
||||||
|
|
||||||
|
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
|
||||||
|
dialogBuilder.setMessage(R.string.home_clear_history_confirm);
|
||||||
|
|
||||||
|
dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final DialogInterface dialog, final int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogBuilder.setPositiveButton(R.string.button_ok, new AlertDialog.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(final DialogInterface dialog, final int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final ContentResolver cr = context.getContentResolver();
|
||||||
|
BrowserDB.clearHistory(cr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Telemetry.sendUIEvent(TelemetryContract.Event.SANITIZE, TelemetryContract.Method.BUTTON, "history");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dialogBuilder.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
mList = null;
|
||||||
|
mEmptyView = null;
|
||||||
|
mClearHistoryButton = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
|
// Intialize adapter
|
||||||
|
mAdapter = new HistoryAdapter(getActivity());
|
||||||
|
mList.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
// Create callbacks before the initial loader is started
|
||||||
|
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
|
||||||
loadIfVisible();
|
loadIfVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
protected void load() {
|
||||||
// Show most recent panel as the initial panel.
|
getLoaderManager().initLoader(LOADER_ID_HISTORY, null, mCursorLoaderCallbacks);
|
||||||
// Since we detach/attach on config change, this prevents from replacing current fragment.
|
}
|
||||||
if (!initializeRecentPanel) {
|
|
||||||
showMostRecentPanel();
|
private static class HistoryCursorLoader extends SimpleCursorLoader {
|
||||||
initializeRecentPanel = true;
|
// Max number of history results
|
||||||
|
private static final int HISTORY_LIMIT = 100;
|
||||||
|
|
||||||
|
public HistoryCursorLoader(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor loadCursor() {
|
||||||
|
final ContentResolver cr = getContext().getContentResolver();
|
||||||
|
return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void updateUiFromCursor(Cursor c) {
|
||||||
public void onTabChanged(int index) {
|
if (c != null && c.getCount() > 0) {
|
||||||
if (index == mSelectedTab) {
|
mClearHistoryButton.setVisibility(View.VISIBLE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0) {
|
// Cursor is empty, so hide the "Clear browsing history" button,
|
||||||
showMostRecentPanel();
|
// and set the empty view if it hasn't been set already.
|
||||||
} else if (index == 1) {
|
mClearHistoryButton.setVisibility(View.GONE);
|
||||||
showLastTabsPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
mTabWidget.setCurrentTab(index);
|
if (mEmptyView == null) {
|
||||||
mSelectedTab = index;
|
// Set empty panel view. We delay this so that the empty view won't flash.
|
||||||
}
|
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
||||||
|
mEmptyView = emptyViewStub.inflate();
|
||||||
|
|
||||||
@Override
|
final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
emptyIcon.setImageResource(R.drawable.icon_most_recent_empty);
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
|
|
||||||
// Rotation should detach and re-attach to use a different layout.
|
final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text);
|
||||||
if (isVisible()) {
|
emptyText.setText(R.string.home_most_recent_empty);
|
||||||
getFragmentManager().beginTransaction()
|
|
||||||
.detach(this)
|
mList.setEmptyView(mEmptyView);
|
||||||
.attach(this)
|
|
||||||
.commitAllowingStateLoss();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSubPanel(Fragment subPanel) {
|
private static class HistoryAdapter extends MultiTypeCursorAdapter {
|
||||||
final Bundle args = new Bundle();
|
private static final int ROW_HEADER = 0;
|
||||||
args.putBoolean(HomePager.CAN_LOAD_ARG, getCanLoadHint());
|
private static final int ROW_STANDARD = 1;
|
||||||
subPanel.setArguments(args);
|
|
||||||
|
|
||||||
getChildFragmentManager().beginTransaction()
|
private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
|
||||||
.addToBackStack(null).replace(R.id.history_panel_container, subPanel)
|
private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
|
||||||
.commitAllowingStateLoss();
|
|
||||||
|
// For the time sections in history
|
||||||
|
private static final long MS_PER_DAY = 86400000;
|
||||||
|
private static final long MS_PER_WEEK = MS_PER_DAY * 7;
|
||||||
|
|
||||||
|
// The time ranges for each section
|
||||||
|
private static enum MostRecentSection {
|
||||||
|
TODAY,
|
||||||
|
YESTERDAY,
|
||||||
|
WEEK,
|
||||||
|
OLDER
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
// Maps headers in the list with their respective sections
|
||||||
|
private final SparseArray<MostRecentSection> mMostRecentSections;
|
||||||
|
|
||||||
|
public HistoryAdapter(Context context) {
|
||||||
|
super(context, null, VIEW_TYPES, LAYOUT_TYPES);
|
||||||
|
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
// Initialize map of history sections
|
||||||
|
mMostRecentSections = new SparseArray<MostRecentSection>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
final int type = getItemViewType(position);
|
||||||
|
|
||||||
|
// Header items are not in the cursor
|
||||||
|
if (type == ROW_HEADER) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getItem(position - getMostRecentSectionsCountBefore(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
if (mMostRecentSections.get(position) != null) {
|
||||||
|
return ROW_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ROW_STANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(int position) {
|
||||||
|
return (getItemViewType(position) == ROW_STANDARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
// Add the history section headers to the number of reported results.
|
||||||
|
return super.getCount() + mMostRecentSections.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor swapCursor(Cursor cursor) {
|
||||||
|
loadMostRecentSections(cursor);
|
||||||
|
Cursor oldCursor = super.swapCursor(cursor);
|
||||||
|
return oldCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, int position) {
|
||||||
|
final int type = getItemViewType(position);
|
||||||
|
|
||||||
|
if (type == ROW_HEADER) {
|
||||||
|
final MostRecentSection section = mMostRecentSections.get(position);
|
||||||
|
final TextView row = (TextView) view;
|
||||||
|
row.setText(getMostRecentSectionTitle(section));
|
||||||
|
} else {
|
||||||
|
// Account for the most recent section headers
|
||||||
|
position -= getMostRecentSectionsCountBefore(position);
|
||||||
|
final Cursor c = getCursor(position);
|
||||||
|
final TwoLinePageRow row = (TwoLinePageRow) view;
|
||||||
|
row.updateFromCursor(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMostRecentSectionTitle(MostRecentSection section) {
|
||||||
|
switch (section) {
|
||||||
|
case TODAY:
|
||||||
|
return mContext.getString(R.string.history_today_section);
|
||||||
|
case YESTERDAY:
|
||||||
|
return mContext.getString(R.string.history_yesterday_section);
|
||||||
|
case WEEK:
|
||||||
|
return mContext.getString(R.string.history_week_section);
|
||||||
|
case OLDER:
|
||||||
|
return mContext.getString(R.string.history_older_section);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("Unrecognized history section");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMostRecentSectionsCountBefore(int position) {
|
||||||
|
// Account for the number headers before the given position
|
||||||
|
int sectionsBefore = 0;
|
||||||
|
|
||||||
|
final int historySectionsCount = mMostRecentSections.size();
|
||||||
|
for (int i = 0; i < historySectionsCount; i++) {
|
||||||
|
final int sectionPosition = mMostRecentSections.keyAt(i);
|
||||||
|
if (sectionPosition > position) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionsBefore++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectionsBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MostRecentSection getMostRecentSectionForTime(long from, long time) {
|
||||||
|
long delta = from - time;
|
||||||
|
|
||||||
|
if (delta < 0) {
|
||||||
|
return MostRecentSection.TODAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta < MS_PER_DAY) {
|
||||||
|
return MostRecentSection.YESTERDAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta < MS_PER_WEEK) {
|
||||||
|
return MostRecentSection.WEEK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MostRecentSection.OLDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadMostRecentSections(Cursor c) {
|
||||||
|
// Clear any history sections that may have been loaded before.
|
||||||
|
mMostRecentSections.clear();
|
||||||
|
|
||||||
|
if (c == null || !c.moveToFirst()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Date now = new Date();
|
||||||
|
now.setHours(0);
|
||||||
|
now.setMinutes(0);
|
||||||
|
now.setSeconds(0);
|
||||||
|
|
||||||
|
final long today = now.getTime();
|
||||||
|
MostRecentSection section = null;
|
||||||
|
|
||||||
|
do {
|
||||||
|
final int position = c.getPosition();
|
||||||
|
final long time = c.getLong(c.getColumnIndexOrThrow(URLColumns.DATE_LAST_VISITED));
|
||||||
|
final MostRecentSection itemSection = HistoryAdapter.getMostRecentSectionForTime(today, time);
|
||||||
|
|
||||||
|
if (section != itemSection) {
|
||||||
|
section = itemSection;
|
||||||
|
mMostRecentSections.append(position + mMostRecentSections.size(), section);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached the last section, no need to continue
|
||||||
|
if (section == MostRecentSection.OLDER) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (c.moveToNext());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMostRecentPanel() {
|
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
||||||
final MostRecentPanel mostRecentPanel = MostRecentPanel.newInstance();
|
@Override
|
||||||
showSubPanel(mostRecentPanel);
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
}
|
return new HistoryCursorLoader(getActivity());
|
||||||
|
}
|
||||||
|
|
||||||
private void showLastTabsPanel() {
|
@Override
|
||||||
final LastTabsPanel lastTabsPanel = LastTabsPanel.newInstance();
|
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
|
||||||
showSubPanel(lastTabsPanel);
|
mAdapter.swapCursor(c);
|
||||||
|
updateUiFromCursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(Loader<Cursor> loader) {
|
||||||
|
mAdapter.swapCursor(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ public final class HomeConfig {
|
||||||
BOOKMARKS("bookmarks", BookmarksPanel.class),
|
BOOKMARKS("bookmarks", BookmarksPanel.class),
|
||||||
HISTORY("history", HistoryPanel.class),
|
HISTORY("history", HistoryPanel.class),
|
||||||
READING_LIST("reading_list", ReadingListPanel.class),
|
READING_LIST("reading_list", ReadingListPanel.class),
|
||||||
|
RECENT_TABS("recent_tabs", RecentTabsPanel.class),
|
||||||
DYNAMIC("dynamic", DynamicPanel.class);
|
DYNAMIC("dynamic", DynamicPanel.class);
|
||||||
|
|
||||||
private final String mId;
|
private final String mId;
|
||||||
|
@ -1495,6 +1496,7 @@ public final class HomeConfig {
|
||||||
private static final String BOOKMARKS_PANEL_ID = "7f6d419a-cd6c-4e34-b26f-f68b1b551907";
|
private static final String BOOKMARKS_PANEL_ID = "7f6d419a-cd6c-4e34-b26f-f68b1b551907";
|
||||||
private static final String READING_LIST_PANEL_ID = "20f4549a-64ad-4c32-93e4-1dcef792733b";
|
private static final String READING_LIST_PANEL_ID = "20f4549a-64ad-4c32-93e4-1dcef792733b";
|
||||||
private static final String HISTORY_PANEL_ID = "f134bf20-11f7-4867-ab8b-e8e705d7fbe8";
|
private static final String HISTORY_PANEL_ID = "f134bf20-11f7-4867-ab8b-e8e705d7fbe8";
|
||||||
|
private static final String RECENT_TABS_PANEL_ID = "5c2601a5-eedc-4477-b297-ce4cef52adf8";
|
||||||
|
|
||||||
private final HomeConfigBackend mBackend;
|
private final HomeConfigBackend mBackend;
|
||||||
|
|
||||||
|
@ -1550,6 +1552,11 @@ public final class HomeConfig {
|
||||||
id = READING_LIST_PANEL_ID;
|
id = READING_LIST_PANEL_ID;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RECENT_TABS:
|
||||||
|
titleId = R.string.recent_tabs_title;
|
||||||
|
id = RECENT_TABS_PANEL_ID;
|
||||||
|
break;
|
||||||
|
|
||||||
case DYNAMIC:
|
case DYNAMIC:
|
||||||
throw new IllegalArgumentException("createBuiltinPanelConfig() is only for built-in panels");
|
throw new IllegalArgumentException("createBuiltinPanelConfig() is only for built-in panels");
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,19 @@ import android.util.Log;
|
||||||
class HomeConfigPrefsBackend implements HomeConfigBackend {
|
class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||||
private static final String LOGTAG = "GeckoHomeConfigBackend";
|
private static final String LOGTAG = "GeckoHomeConfigBackend";
|
||||||
|
|
||||||
private static final String PREFS_CONFIG_KEY = "home_panels";
|
// Increment this to trigger a migration.
|
||||||
|
private static final int VERSION = 1;
|
||||||
|
|
||||||
|
// This key was originally used to store only an array of panel configs.
|
||||||
|
private static final String PREFS_CONFIG_KEY_OLD = "home_panels";
|
||||||
|
|
||||||
|
// This key is now used to store a version number with the array of panel configs.
|
||||||
|
private static final String PREFS_CONFIG_KEY = "home_panels_with_version";
|
||||||
|
|
||||||
|
// Keys used with JSON object stored in prefs.
|
||||||
|
private static final String JSON_KEY_PANELS = "panels";
|
||||||
|
private static final String JSON_KEY_VERSION = "version";
|
||||||
|
|
||||||
private static final String PREFS_LOCALE_KEY = "home_locale";
|
private static final String PREFS_LOCALE_KEY = "home_locale";
|
||||||
|
|
||||||
private static final String RELOAD_BROADCAST = "HomeConfigPrefsBackend:Reload";
|
private static final String RELOAD_BROADCAST = "HomeConfigPrefsBackend:Reload";
|
||||||
|
@ -45,6 +57,8 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||||
private ReloadBroadcastReceiver mReloadBroadcastReceiver;
|
private ReloadBroadcastReceiver mReloadBroadcastReceiver;
|
||||||
private OnReloadListener mReloadListener;
|
private OnReloadListener mReloadListener;
|
||||||
|
|
||||||
|
private static boolean sMigrationDone = false;
|
||||||
|
|
||||||
public HomeConfigPrefsBackend(Context context) {
|
public HomeConfigPrefsBackend(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
@ -68,22 +82,109 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
final PanelConfig historyEntry = createBuiltinPanelConfig(mContext, PanelType.HISTORY);
|
final PanelConfig historyEntry = createBuiltinPanelConfig(mContext, PanelType.HISTORY);
|
||||||
|
final PanelConfig recentTabsEntry = createBuiltinPanelConfig(mContext, PanelType.RECENT_TABS);
|
||||||
|
|
||||||
// On tablets, the history panel is the last.
|
// On tablets, the history panel is the last.
|
||||||
// On phones, the history panel is the first one.
|
// On phones, the history panel is the first one.
|
||||||
if (HardwareUtils.isTablet()) {
|
if (HardwareUtils.isTablet()) {
|
||||||
panelConfigs.add(historyEntry);
|
panelConfigs.add(historyEntry);
|
||||||
|
panelConfigs.add(recentTabsEntry);
|
||||||
} else {
|
} else {
|
||||||
panelConfigs.add(0, historyEntry);
|
panelConfigs.add(0, historyEntry);
|
||||||
|
panelConfigs.add(0, recentTabsEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new State(panelConfigs, true);
|
return new State(panelConfigs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates JSON config data storage.
|
||||||
|
*
|
||||||
|
* @param context Context used to get shared preferences and create built-in panel.
|
||||||
|
* @param jsonString String currently stored in preferences.
|
||||||
|
*
|
||||||
|
* @return JSONArray array representing new set of panel configs.
|
||||||
|
*/
|
||||||
|
private static synchronized JSONArray maybePerformMigration(Context context, String jsonString) throws JSONException {
|
||||||
|
// If the migration is already done, we're at the current version.
|
||||||
|
if (sMigrationDone) {
|
||||||
|
final JSONObject json = new JSONObject(jsonString);
|
||||||
|
return json.getJSONArray(JSON_KEY_PANELS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we only do this version check once.
|
||||||
|
sMigrationDone = true;
|
||||||
|
|
||||||
|
final JSONArray originalJsonPanels;
|
||||||
|
final int version;
|
||||||
|
|
||||||
|
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(context);
|
||||||
|
if (prefs.contains(PREFS_CONFIG_KEY_OLD)) {
|
||||||
|
// Our original implementation did not contain versioning, so this is implicitly version 0.
|
||||||
|
originalJsonPanels = new JSONArray(jsonString);
|
||||||
|
version = 0;
|
||||||
|
} else {
|
||||||
|
final JSONObject json = new JSONObject(jsonString);
|
||||||
|
originalJsonPanels = json.getJSONArray(JSON_KEY_PANELS);
|
||||||
|
version = json.getInt(JSON_KEY_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version == VERSION) {
|
||||||
|
return originalJsonPanels;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(LOGTAG, "Performing migration");
|
||||||
|
|
||||||
|
final JSONArray newJsonPanels = new JSONArray();
|
||||||
|
final SharedPreferences.Editor prefsEditor = prefs.edit();
|
||||||
|
|
||||||
|
for (int v = version + 1; v <= VERSION; v++) {
|
||||||
|
Log.d(LOGTAG, "Migrating to version = " + v);
|
||||||
|
|
||||||
|
switch (v) {
|
||||||
|
case 1:
|
||||||
|
// Add "Recent Tabs" panel
|
||||||
|
final PanelConfig recentTabsConfig = createBuiltinPanelConfig(context, PanelType.RECENT_TABS);
|
||||||
|
final JSONObject jsonRecentTabsConfig = recentTabsConfig.toJSON();
|
||||||
|
|
||||||
|
// Add the new panel to the front of the array on phones.
|
||||||
|
if (!HardwareUtils.isTablet()) {
|
||||||
|
newJsonPanels.put(jsonRecentTabsConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the original panel configs.
|
||||||
|
final int count = originalJsonPanels.length();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final JSONObject jsonPanelConfig = originalJsonPanels.getJSONObject(i);
|
||||||
|
newJsonPanels.put(jsonPanelConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new panel to the end of the array on tablets.
|
||||||
|
if (HardwareUtils.isTablet()) {
|
||||||
|
newJsonPanels.put(jsonRecentTabsConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the old pref key.
|
||||||
|
prefsEditor.remove(PREFS_CONFIG_KEY_OLD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the new panel config and the new version number.
|
||||||
|
final JSONObject newJson = new JSONObject();
|
||||||
|
newJson.put(JSON_KEY_PANELS, newJsonPanels);
|
||||||
|
newJson.put(JSON_KEY_VERSION, VERSION);
|
||||||
|
|
||||||
|
prefsEditor.putString(PREFS_CONFIG_KEY, newJson.toString());
|
||||||
|
prefsEditor.commit();
|
||||||
|
|
||||||
|
return newJsonPanels;
|
||||||
|
}
|
||||||
|
|
||||||
private State loadConfigFromString(String jsonString) {
|
private State loadConfigFromString(String jsonString) {
|
||||||
final JSONArray jsonPanelConfigs;
|
final JSONArray jsonPanelConfigs;
|
||||||
try {
|
try {
|
||||||
jsonPanelConfigs = new JSONArray(jsonString);
|
jsonPanelConfigs = maybePerformMigration(mContext, jsonString);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
Log.e(LOGTAG, "Error loading the list of home panels from JSON prefs", e);
|
Log.e(LOGTAG, "Error loading the list of home panels from JSON prefs", e);
|
||||||
|
|
||||||
|
@ -110,7 +211,9 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||||
@Override
|
@Override
|
||||||
public State load() {
|
public State load() {
|
||||||
final SharedPreferences prefs = getSharedPreferences();
|
final SharedPreferences prefs = getSharedPreferences();
|
||||||
final String jsonString = prefs.getString(PREFS_CONFIG_KEY, null);
|
|
||||||
|
final String key = (prefs.contains(PREFS_CONFIG_KEY_OLD) ? PREFS_CONFIG_KEY_OLD : PREFS_CONFIG_KEY);
|
||||||
|
final String jsonString = prefs.getString(key, null);
|
||||||
|
|
||||||
final State configState;
|
final State configState;
|
||||||
if (TextUtils.isEmpty(jsonString)) {
|
if (TextUtils.isEmpty(jsonString)) {
|
||||||
|
@ -142,7 +245,15 @@ class HomeConfigPrefsBackend implements HomeConfigBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.putString(PREFS_CONFIG_KEY, jsonPanelConfigs.toString());
|
try {
|
||||||
|
final JSONObject json = new JSONObject();
|
||||||
|
json.put(JSON_KEY_PANELS, jsonPanelConfigs);
|
||||||
|
json.put(JSON_KEY_VERSION, VERSION);
|
||||||
|
|
||||||
|
editor.putString(PREFS_CONFIG_KEY, json.toString());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.e(LOGTAG, "Exception saving PanelConfig state", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.putString(PREFS_LOCALE_KEY, Locale.getDefault().toString());
|
editor.putString(PREFS_LOCALE_KEY, Locale.getDefault().toString());
|
||||||
|
|
|
@ -68,8 +68,7 @@ public class HomePager extends ViewPager {
|
||||||
static final String LIST_TAG_BOOKMARKS = "bookmarks";
|
static final String LIST_TAG_BOOKMARKS = "bookmarks";
|
||||||
static final String LIST_TAG_READING_LIST = "reading_list";
|
static final String LIST_TAG_READING_LIST = "reading_list";
|
||||||
static final String LIST_TAG_TOP_SITES = "top_sites";
|
static final String LIST_TAG_TOP_SITES = "top_sites";
|
||||||
static final String LIST_TAG_MOST_RECENT = "most_recent";
|
static final String LIST_TAG_RECENT_TABS = "recent_tabs";
|
||||||
static final String LIST_TAG_LAST_TABS = "last_tabs";
|
|
||||||
static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
|
static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
|
||||||
|
|
||||||
public interface OnUrlOpenListener {
|
public interface OnUrlOpenListener {
|
||||||
|
|
|
@ -1,430 +0,0 @@
|
||||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.gecko.home;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
|
|
||||||
import org.mozilla.gecko.R;
|
|
||||||
import org.mozilla.gecko.Telemetry;
|
|
||||||
import org.mozilla.gecko.TelemetryContract;
|
|
||||||
import org.mozilla.gecko.db.BrowserContract.Combined;
|
|
||||||
import org.mozilla.gecko.db.BrowserDB;
|
|
||||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
|
||||||
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|
||||||
import android.support.v4.content.Loader;
|
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewStub;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fragment that displays recent history in a ListView.
|
|
||||||
*/
|
|
||||||
public class MostRecentPanel extends HomeFragment {
|
|
||||||
// Logging tag name
|
|
||||||
private static final String LOGTAG = "GeckoMostRecentPanel";
|
|
||||||
|
|
||||||
// Cursor loader ID for history query
|
|
||||||
private static final int LOADER_ID_HISTORY = 0;
|
|
||||||
|
|
||||||
// Adapter for the list of search results
|
|
||||||
private MostRecentAdapter mAdapter;
|
|
||||||
|
|
||||||
// The view shown by the fragment.
|
|
||||||
private HomeListView mList;
|
|
||||||
|
|
||||||
// The button view for clearing browsing history.
|
|
||||||
private View mClearHistoryButton;
|
|
||||||
|
|
||||||
// Reference to the View to display when there are no results.
|
|
||||||
private View mEmptyView;
|
|
||||||
|
|
||||||
// Callbacks used for the search and favicon cursor loaders
|
|
||||||
private CursorLoaderCallbacks mCursorLoaderCallbacks;
|
|
||||||
|
|
||||||
// On URL open listener
|
|
||||||
private OnUrlOpenListener mUrlOpenListener;
|
|
||||||
|
|
||||||
public static MostRecentPanel newInstance() {
|
|
||||||
return new MostRecentPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MostRecentPanel() {
|
|
||||||
mUrlOpenListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(Activity activity) {
|
|
||||||
super.onAttach(activity);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mUrlOpenListener = (OnUrlOpenListener) activity;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new ClassCastException(activity.toString()
|
|
||||||
+ " must implement HomePager.OnUrlOpenListener");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetach() {
|
|
||||||
super.onDetach();
|
|
||||||
mUrlOpenListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.home_most_recent_panel, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
mList = (HomeListView) view.findViewById(R.id.list);
|
|
||||||
mList.setTag(HomePager.LIST_TAG_MOST_RECENT);
|
|
||||||
|
|
||||||
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
position -= mAdapter.getMostRecentSectionsCountBefore(position);
|
|
||||||
final Cursor c = mAdapter.getCursor(position);
|
|
||||||
final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
|
|
||||||
|
|
||||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
|
|
||||||
|
|
||||||
// This item is a TwoLinePageRow, so we allow switch-to-tab.
|
|
||||||
mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
|
|
||||||
@Override
|
|
||||||
public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
|
|
||||||
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
|
|
||||||
info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
|
|
||||||
info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
|
|
||||||
info.display = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.DISPLAY));
|
|
||||||
info.historyId = cursor.getInt(cursor.getColumnIndexOrThrow(Combined.HISTORY_ID));
|
|
||||||
final int bookmarkIdCol = cursor.getColumnIndexOrThrow(Combined.BOOKMARK_ID);
|
|
||||||
if (cursor.isNull(bookmarkIdCol)) {
|
|
||||||
// If this is a combined cursor, we may get a history item without a
|
|
||||||
// bookmark, in which case the bookmarks ID column value will be null.
|
|
||||||
info.bookmarkId = -1;
|
|
||||||
} else {
|
|
||||||
info.bookmarkId = cursor.getInt(bookmarkIdCol);
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
registerForContextMenu(mList);
|
|
||||||
|
|
||||||
mClearHistoryButton = view.findViewById(R.id.clear_history_button);
|
|
||||||
mClearHistoryButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
final Context context = getActivity();
|
|
||||||
|
|
||||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
|
|
||||||
dialogBuilder.setMessage(R.string.home_clear_history_confirm);
|
|
||||||
|
|
||||||
dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(final DialogInterface dialog, final int which) {
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogBuilder.setPositiveButton(R.string.button_ok, new AlertDialog.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(final DialogInterface dialog, final int which) {
|
|
||||||
dialog.dismiss();
|
|
||||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
final ContentResolver cr = context.getContentResolver();
|
|
||||||
BrowserDB.clearHistory(cr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Telemetry.sendUIEvent(TelemetryContract.Event.SANITIZE, TelemetryContract.Method.BUTTON, "history");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dialogBuilder.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
mList = null;
|
|
||||||
mEmptyView = null;
|
|
||||||
mClearHistoryButton = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
// Intialize adapter
|
|
||||||
mAdapter = new MostRecentAdapter(getActivity());
|
|
||||||
mList.setAdapter(mAdapter);
|
|
||||||
|
|
||||||
// Create callbacks before the initial loader is started
|
|
||||||
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
|
|
||||||
loadIfVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void load() {
|
|
||||||
getLoaderManager().initLoader(LOADER_ID_HISTORY, null, mCursorLoaderCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MostRecentCursorLoader extends SimpleCursorLoader {
|
|
||||||
// Max number of history results
|
|
||||||
private static final int HISTORY_LIMIT = 100;
|
|
||||||
|
|
||||||
public MostRecentCursorLoader(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor loadCursor() {
|
|
||||||
final ContentResolver cr = getContext().getContentResolver();
|
|
||||||
return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUiFromCursor(Cursor c) {
|
|
||||||
if (c != null && c.getCount() > 0) {
|
|
||||||
mClearHistoryButton.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cursor is empty, so hide the "Clear browsing history" button,
|
|
||||||
// and set the empty view if it hasn't been set already.
|
|
||||||
mClearHistoryButton.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (mEmptyView == null) {
|
|
||||||
// Set empty panel view. We delay this so that the empty view won't flash.
|
|
||||||
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
|
||||||
mEmptyView = emptyViewStub.inflate();
|
|
||||||
|
|
||||||
final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image);
|
|
||||||
emptyIcon.setImageResource(R.drawable.icon_most_recent_empty);
|
|
||||||
|
|
||||||
final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text);
|
|
||||||
emptyText.setText(R.string.home_most_recent_empty);
|
|
||||||
|
|
||||||
mList.setEmptyView(mEmptyView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MostRecentAdapter extends MultiTypeCursorAdapter {
|
|
||||||
private static final int ROW_HEADER = 0;
|
|
||||||
private static final int ROW_STANDARD = 1;
|
|
||||||
|
|
||||||
private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
|
|
||||||
private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
|
|
||||||
|
|
||||||
// For the time sections in history
|
|
||||||
private static final long MS_PER_DAY = 86400000;
|
|
||||||
private static final long MS_PER_WEEK = MS_PER_DAY * 7;
|
|
||||||
|
|
||||||
// The time ranges for each section
|
|
||||||
private static enum MostRecentSection {
|
|
||||||
TODAY,
|
|
||||||
YESTERDAY,
|
|
||||||
WEEK,
|
|
||||||
OLDER
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
// Maps headers in the list with their respective sections
|
|
||||||
private final SparseArray<MostRecentSection> mMostRecentSections;
|
|
||||||
|
|
||||||
public MostRecentAdapter(Context context) {
|
|
||||||
super(context, null, VIEW_TYPES, LAYOUT_TYPES);
|
|
||||||
|
|
||||||
mContext = context;
|
|
||||||
|
|
||||||
// Initialize map of history sections
|
|
||||||
mMostRecentSections = new SparseArray<MostRecentSection>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getItem(int position) {
|
|
||||||
final int type = getItemViewType(position);
|
|
||||||
|
|
||||||
// Header items are not in the cursor
|
|
||||||
if (type == ROW_HEADER) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.getItem(position - getMostRecentSectionsCountBefore(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
if (mMostRecentSections.get(position) != null) {
|
|
||||||
return ROW_HEADER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ROW_STANDARD;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled(int position) {
|
|
||||||
return (getItemViewType(position) == ROW_STANDARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
// Add the history section headers to the number of reported results.
|
|
||||||
return super.getCount() + mMostRecentSections.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor swapCursor(Cursor cursor) {
|
|
||||||
loadMostRecentSections(cursor);
|
|
||||||
Cursor oldCursor = super.swapCursor(cursor);
|
|
||||||
return oldCursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, int position) {
|
|
||||||
final int type = getItemViewType(position);
|
|
||||||
|
|
||||||
if (type == ROW_HEADER) {
|
|
||||||
final MostRecentSection section = mMostRecentSections.get(position);
|
|
||||||
final TextView row = (TextView) view;
|
|
||||||
row.setText(getMostRecentSectionTitle(section));
|
|
||||||
} else {
|
|
||||||
// Account for the most recent section headers
|
|
||||||
position -= getMostRecentSectionsCountBefore(position);
|
|
||||||
final Cursor c = getCursor(position);
|
|
||||||
final TwoLinePageRow row = (TwoLinePageRow) view;
|
|
||||||
row.updateFromCursor(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getMostRecentSectionTitle(MostRecentSection section) {
|
|
||||||
switch (section) {
|
|
||||||
case TODAY:
|
|
||||||
return mContext.getString(R.string.history_today_section);
|
|
||||||
case YESTERDAY:
|
|
||||||
return mContext.getString(R.string.history_yesterday_section);
|
|
||||||
case WEEK:
|
|
||||||
return mContext.getString(R.string.history_week_section);
|
|
||||||
case OLDER:
|
|
||||||
return mContext.getString(R.string.history_older_section);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalStateException("Unrecognized history section");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getMostRecentSectionsCountBefore(int position) {
|
|
||||||
// Account for the number headers before the given position
|
|
||||||
int sectionsBefore = 0;
|
|
||||||
|
|
||||||
final int historySectionsCount = mMostRecentSections.size();
|
|
||||||
for (int i = 0; i < historySectionsCount; i++) {
|
|
||||||
final int sectionPosition = mMostRecentSections.keyAt(i);
|
|
||||||
if (sectionPosition > position) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sectionsBefore++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sectionsBefore;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MostRecentSection getMostRecentSectionForTime(long from, long time) {
|
|
||||||
long delta = from - time;
|
|
||||||
|
|
||||||
if (delta < 0) {
|
|
||||||
return MostRecentSection.TODAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta < MS_PER_DAY) {
|
|
||||||
return MostRecentSection.YESTERDAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta < MS_PER_WEEK) {
|
|
||||||
return MostRecentSection.WEEK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MostRecentSection.OLDER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadMostRecentSections(Cursor c) {
|
|
||||||
// Clear any history sections that may have been loaded before.
|
|
||||||
mMostRecentSections.clear();
|
|
||||||
|
|
||||||
if (c == null || !c.moveToFirst()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Date now = new Date();
|
|
||||||
now.setHours(0);
|
|
||||||
now.setMinutes(0);
|
|
||||||
now.setSeconds(0);
|
|
||||||
|
|
||||||
final long today = now.getTime();
|
|
||||||
MostRecentSection section = null;
|
|
||||||
|
|
||||||
do {
|
|
||||||
final int position = c.getPosition();
|
|
||||||
final long time = c.getLong(c.getColumnIndexOrThrow(URLColumns.DATE_LAST_VISITED));
|
|
||||||
final MostRecentSection itemSection = MostRecentAdapter.getMostRecentSectionForTime(today, time);
|
|
||||||
|
|
||||||
if (section != itemSection) {
|
|
||||||
section = itemSection;
|
|
||||||
mMostRecentSections.append(position + mMostRecentSections.size(), section);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reached the last section, no need to continue
|
|
||||||
if (section == MostRecentSection.OLDER) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (c.moveToNext());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
|
||||||
return new MostRecentCursorLoader(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
|
|
||||||
mAdapter.swapCursor(c);
|
|
||||||
updateUiFromCursor(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(Loader<Cursor> loader) {
|
|
||||||
mAdapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,16 +6,25 @@
|
||||||
package org.mozilla.gecko.home;
|
package org.mozilla.gecko.home;
|
||||||
|
|
||||||
import org.mozilla.gecko.AboutPages;
|
import org.mozilla.gecko.AboutPages;
|
||||||
|
import org.mozilla.gecko.EventDispatcher;
|
||||||
|
import org.mozilla.gecko.GeckoAppShell;
|
||||||
|
import org.mozilla.gecko.GeckoEvent;
|
||||||
import org.mozilla.gecko.GeckoProfile;
|
import org.mozilla.gecko.GeckoProfile;
|
||||||
import org.mozilla.gecko.R;
|
import org.mozilla.gecko.R;
|
||||||
import org.mozilla.gecko.SessionParser;
|
import org.mozilla.gecko.SessionParser;
|
||||||
import org.mozilla.gecko.Telemetry;
|
import org.mozilla.gecko.Telemetry;
|
||||||
import org.mozilla.gecko.TelemetryContract;
|
import org.mozilla.gecko.TelemetryContract;
|
||||||
import org.mozilla.gecko.db.BrowserContract.Combined;
|
import org.mozilla.gecko.db.BrowserContract.CommonColumns;
|
||||||
|
import org.mozilla.gecko.db.BrowserContract.URLColumns;
|
||||||
import org.mozilla.gecko.home.HomePager.OnNewTabsListener;
|
import org.mozilla.gecko.home.HomePager.OnNewTabsListener;
|
||||||
|
import org.mozilla.gecko.util.EventCallback;
|
||||||
|
import org.mozilla.gecko.util.NativeEventListener;
|
||||||
|
import org.mozilla.gecko.util.NativeJSObject;
|
||||||
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.MatrixCursor;
|
import android.database.MatrixCursor;
|
||||||
import android.database.MatrixCursor.RowBuilder;
|
import android.database.MatrixCursor.RowBuilder;
|
||||||
|
@ -23,6 +32,7 @@ import android.os.Bundle;
|
||||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||||
import android.support.v4.content.Loader;
|
import android.support.v4.content.Loader;
|
||||||
import android.support.v4.widget.CursorAdapter;
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -34,25 +44,20 @@ import android.widget.TextView;
|
||||||
/**
|
/**
|
||||||
* Fragment that displays tabs from last session in a ListView.
|
* Fragment that displays tabs from last session in a ListView.
|
||||||
*/
|
*/
|
||||||
public class LastTabsPanel extends HomeFragment {
|
public class RecentTabsPanel extends HomeFragment
|
||||||
|
implements NativeEventListener {
|
||||||
// Logging tag name
|
// Logging tag name
|
||||||
private static final String LOGTAG = "GeckoLastTabsPanel";
|
private static final String LOGTAG = "GeckoRecentTabsPanel";
|
||||||
|
|
||||||
// Cursor loader ID for the session parser
|
// Cursor loader ID for the loader that loads recent tabs
|
||||||
private static final int LOADER_ID_LAST_TABS = 0;
|
private static final int LOADER_ID_RECENT_TABS = 0;
|
||||||
|
|
||||||
// Adapter for the list of search results
|
// Adapter for the list of recent tabs.
|
||||||
private LastTabsAdapter mAdapter;
|
private RecentTabsAdapter mAdapter;
|
||||||
|
|
||||||
// The view shown by the fragment.
|
// The view shown by the fragment.
|
||||||
private HomeListView mList;
|
private HomeListView mList;
|
||||||
|
|
||||||
// The title for this HomeFragment panel.
|
|
||||||
private TextView mTitle;
|
|
||||||
|
|
||||||
// The button view for restoring tabs from last session.
|
|
||||||
private View mRestoreButton;
|
|
||||||
|
|
||||||
// Reference to the View to display when there are no results.
|
// Reference to the View to display when there are no results.
|
||||||
private View mEmptyView;
|
private View mEmptyView;
|
||||||
|
|
||||||
|
@ -62,12 +67,25 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
// On new tabs listener
|
// On new tabs listener
|
||||||
private OnNewTabsListener mNewTabsListener;
|
private OnNewTabsListener mNewTabsListener;
|
||||||
|
|
||||||
public static LastTabsPanel newInstance() {
|
// Recently closed tabs from gecko
|
||||||
return new LastTabsPanel();
|
private ClosedTab[] mClosedTabs;
|
||||||
|
|
||||||
|
private static final class ClosedTab {
|
||||||
|
public final String url;
|
||||||
|
public final String title;
|
||||||
|
|
||||||
|
public ClosedTab(String url, String title) {
|
||||||
|
this.url = url;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastTabsPanel() {
|
public static final class RecentTabs implements URLColumns, CommonColumns {
|
||||||
mNewTabsListener = null;
|
public static final String TYPE = "type";
|
||||||
|
|
||||||
|
public static final int TYPE_HEADER = 0;
|
||||||
|
public static final int TYPE_LAST_TIME = 1;
|
||||||
|
public static final int TYPE_CLOSED = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,18 +109,13 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.home_last_tabs_panel, container, false);
|
return inflater.inflate(R.layout.home_recent_tabs_panel, container, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
mTitle = (TextView) view.findViewById(R.id.title);
|
|
||||||
if (mTitle != null) {
|
|
||||||
mTitle.setText(R.string.home_last_tabs_title);
|
|
||||||
}
|
|
||||||
|
|
||||||
mList = (HomeListView) view.findViewById(R.id.list);
|
mList = (HomeListView) view.findViewById(R.id.list);
|
||||||
mList.setTag(HomePager.LIST_TAG_LAST_TABS);
|
mList.setTag(HomePager.LIST_TAG_RECENT_TABS);
|
||||||
|
|
||||||
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,7 +127,7 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
|
|
||||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
|
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
|
||||||
|
|
||||||
final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
|
final String url = c.getString(c.getColumnIndexOrThrow(RecentTabs.URL));
|
||||||
mNewTabsListener.onNewTabs(new String[] { url });
|
mNewTabsListener.onNewTabs(new String[] { url });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -123,30 +136,39 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
@Override
|
@Override
|
||||||
public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
|
public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
|
||||||
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
|
final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
|
||||||
info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL));
|
info.url = cursor.getString(cursor.getColumnIndexOrThrow(RecentTabs.URL));
|
||||||
info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE));
|
info.title = cursor.getString(cursor.getColumnIndexOrThrow(RecentTabs.TITLE));
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
registerForContextMenu(mList);
|
registerForContextMenu(mList);
|
||||||
|
|
||||||
mRestoreButton = view.findViewById(R.id.open_all_tabs_button);
|
EventDispatcher.getInstance().registerGeckoThreadListener(this, "ClosedTabs:Data");
|
||||||
mRestoreButton.setOnClickListener(new View.OnClickListener() {
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("ClosedTabs:StartNotifications", null));
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
openAllTabs();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
mList = null;
|
mList = null;
|
||||||
mTitle = null;
|
|
||||||
mEmptyView = null;
|
mEmptyView = null;
|
||||||
mRestoreButton = null;
|
|
||||||
|
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "ClosedTabs:Data");
|
||||||
|
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("ClosedTabs:StopNotifications", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
// Detach and reattach the fragment as the layout changes.
|
||||||
|
if (isVisible()) {
|
||||||
|
getFragmentManager().beginTransaction()
|
||||||
|
.detach(this)
|
||||||
|
.attach(this)
|
||||||
|
.commitAllowingStateLoss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,7 +176,7 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
// Intialize adapter
|
// Intialize adapter
|
||||||
mAdapter = new LastTabsAdapter(getActivity());
|
mAdapter = new RecentTabsAdapter(getActivity());
|
||||||
mList.setAdapter(mAdapter);
|
mList.setAdapter(mAdapter);
|
||||||
|
|
||||||
// Create callbacks before the initial loader is started
|
// Create callbacks before the initial loader is started
|
||||||
|
@ -164,20 +186,9 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
|
|
||||||
private void updateUiFromCursor(Cursor c) {
|
private void updateUiFromCursor(Cursor c) {
|
||||||
if (c != null && c.getCount() > 0) {
|
if (c != null && c.getCount() > 0) {
|
||||||
if (mTitle != null) {
|
|
||||||
mTitle.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
mRestoreButton.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cursor is empty, so hide the title and set the
|
|
||||||
// empty view if it hasn't been set already.
|
|
||||||
if (mTitle != null) {
|
|
||||||
mTitle.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
mRestoreButton.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
if (mEmptyView == null) {
|
if (mEmptyView == null) {
|
||||||
// Set empty panel view. We delay this so that the empty view won't flash.
|
// Set empty panel view. We delay this so that the empty view won't flash.
|
||||||
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
||||||
|
@ -195,7 +206,32 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void load() {
|
protected void load() {
|
||||||
getLoaderManager().initLoader(LOADER_ID_LAST_TABS, null, mCursorLoaderCallbacks);
|
getLoaderManager().initLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
|
||||||
|
final NativeJSObject[] tabs = message.getObjectArray("tabs");
|
||||||
|
final int length = tabs.length;
|
||||||
|
|
||||||
|
final ClosedTab[] closedTabs = new ClosedTab[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final NativeJSObject tab = tabs[i];
|
||||||
|
closedTabs[i] = new ClosedTab(tab.getString("url"), tab.getString("title"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only modify mClosedTabs on the UI thread
|
||||||
|
ThreadUtils.postToUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mClosedTabs = closedTabs;
|
||||||
|
|
||||||
|
// Reload the cursor to show recently closed tabs.
|
||||||
|
if (mClosedTabs.length > 0 && canLoad()) {
|
||||||
|
getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openAllTabs() {
|
private void openAllTabs() {
|
||||||
|
@ -207,7 +243,7 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
final String[] urls = new String[c.getCount()];
|
final String[] urls = new String[c.getCount()];
|
||||||
|
|
||||||
do {
|
do {
|
||||||
urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(Combined.URL));
|
urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(RecentTabs.URL));
|
||||||
} while (c.moveToNext());
|
} while (c.moveToNext());
|
||||||
|
|
||||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON);
|
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON);
|
||||||
|
@ -215,24 +251,52 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
mNewTabsListener.onNewTabs(urls);
|
mNewTabsListener.onNewTabs(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LastTabsCursorLoader extends SimpleCursorLoader {
|
private static class RecentTabsCursorLoader extends SimpleCursorLoader {
|
||||||
public LastTabsCursorLoader(Context context) {
|
private final ClosedTab[] closedTabs;
|
||||||
|
|
||||||
|
public RecentTabsCursorLoader(Context context, ClosedTab[] closedTabs) {
|
||||||
super(context);
|
super(context);
|
||||||
|
this.closedTabs = closedTabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRow(MatrixCursor c, String url, String title, int type) {
|
||||||
|
final RowBuilder row = c.newRow();
|
||||||
|
row.add(-1);
|
||||||
|
row.add(url);
|
||||||
|
row.add(title);
|
||||||
|
row.add(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cursor loadCursor() {
|
public Cursor loadCursor() {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
|
|
||||||
|
final MatrixCursor c = new MatrixCursor(new String[] { RecentTabs._ID,
|
||||||
|
RecentTabs.URL,
|
||||||
|
RecentTabs.TITLE,
|
||||||
|
RecentTabs.TYPE });
|
||||||
|
|
||||||
|
if (closedTabs != null && closedTabs.length > 0) {
|
||||||
|
addRow(c, null, context.getString(R.string.home_closed_tabs_title), RecentTabs.TYPE_HEADER);
|
||||||
|
|
||||||
|
final int length = closedTabs.length;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final String url = closedTabs[i].url;
|
||||||
|
|
||||||
|
// Don't show recent tabs for about:home
|
||||||
|
if (!AboutPages.isAboutHome(url)) {
|
||||||
|
addRow(c, url, closedTabs[i].title, RecentTabs.TYPE_CLOSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final String jsonString = GeckoProfile.get(context).readSessionFile(true);
|
final String jsonString = GeckoProfile.get(context).readSessionFile(true);
|
||||||
if (jsonString == null) {
|
if (jsonString == null) {
|
||||||
// No previous session data
|
// No previous session data
|
||||||
return null;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
final MatrixCursor c = new MatrixCursor(new String[] { Combined._ID,
|
final int count = c.getCount();
|
||||||
Combined.URL,
|
|
||||||
Combined.TITLE });
|
|
||||||
|
|
||||||
new SessionParser() {
|
new SessionParser() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -244,12 +308,12 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final RowBuilder row = c.newRow();
|
// If this is the first tab we're reading, add a header.
|
||||||
row.add(-1);
|
if (c.getCount() == count) {
|
||||||
row.add(url);
|
addRow(c, null, context.getString(R.string.home_last_tabs_title), RecentTabs.TYPE_HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
final String title = tab.getTitle();
|
addRow(c, url, tab.getTitle(), RecentTabs.TYPE_LAST_TIME);
|
||||||
row.add(title);
|
|
||||||
}
|
}
|
||||||
}.parse(jsonString);
|
}.parse(jsonString);
|
||||||
|
|
||||||
|
@ -257,26 +321,52 @@ public class LastTabsPanel extends HomeFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LastTabsAdapter extends CursorAdapter {
|
private static class RecentTabsAdapter extends MultiTypeCursorAdapter {
|
||||||
public LastTabsAdapter(Context context) {
|
private static final int ROW_HEADER = 0;
|
||||||
super(context, null, 0);
|
private static final int ROW_STANDARD = 1;
|
||||||
|
|
||||||
|
private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
|
||||||
|
private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
|
||||||
|
|
||||||
|
public RecentTabsAdapter(Context context) {
|
||||||
|
super(context, null, VIEW_TYPES, LAYOUT_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
final Cursor c = getCursor(position);
|
||||||
|
final int type = c.getInt(c.getColumnIndexOrThrow(RecentTabs.TYPE));
|
||||||
|
|
||||||
|
if (type == RecentTabs.TYPE_HEADER) {
|
||||||
|
return ROW_HEADER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ROW_STANDARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled(int position) {
|
||||||
|
return (getItemViewType(position) != ROW_HEADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
public void bindView(View view, Context context, int position) {
|
||||||
((TwoLinePageRow) view).updateFromCursor(cursor);
|
final int itemType = getItemViewType(position);
|
||||||
}
|
final Cursor c = getCursor(position);
|
||||||
|
|
||||||
@Override
|
if (itemType == ROW_HEADER) {
|
||||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
final String title = c.getString(c.getColumnIndexOrThrow(RecentTabs.TITLE));
|
||||||
return LayoutInflater.from(context).inflate(R.layout.home_item_row, parent, false);
|
final TextView textView = (TextView) view;
|
||||||
}
|
textView.setText(title);
|
||||||
|
} else if (itemType == ROW_STANDARD) {
|
||||||
|
final TwoLinePageRow pageRow = (TwoLinePageRow) view;
|
||||||
|
pageRow.updateFromCursor(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
|
||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
return new LastTabsCursorLoader(getActivity());
|
return new RecentTabsCursorLoader(getActivity(), mClosedTabs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -11,6 +11,7 @@
|
||||||
<!ENTITY bookmarks_title "Bookmarks">
|
<!ENTITY bookmarks_title "Bookmarks">
|
||||||
<!ENTITY history_title "History">
|
<!ENTITY history_title "History">
|
||||||
<!ENTITY reading_list_title "Reading List">
|
<!ENTITY reading_list_title "Reading List">
|
||||||
|
<!ENTITY recent_tabs_title "Recent Tabs">
|
||||||
|
|
||||||
<!ENTITY switch_to_tab "Switch to tab">
|
<!ENTITY switch_to_tab "Switch to tab">
|
||||||
|
|
||||||
|
@ -349,10 +350,10 @@ size. -->
|
||||||
<!ENTITY home_clear_history_button "Clear browsing history">
|
<!ENTITY home_clear_history_button "Clear browsing history">
|
||||||
<!ENTITY home_clear_history_confirm "Are you sure you want to clear your history?">
|
<!ENTITY home_clear_history_confirm "Are you sure you want to clear your history?">
|
||||||
<!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
|
<!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
|
||||||
|
<!ENTITY home_closed_tabs_title "Recently closed tabs">
|
||||||
<!ENTITY home_last_tabs_title "Tabs from last time">
|
<!ENTITY home_last_tabs_title "Tabs from last time">
|
||||||
<!ENTITY home_last_tabs_open "Open all tabs from last time">
|
<!ENTITY home_last_tabs_open "Open all tabs from last time">
|
||||||
<!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
|
<!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
|
||||||
<!ENTITY home_most_recent_title "Most recent">
|
|
||||||
<!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
|
<!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
|
||||||
<!ENTITY home_reading_list_empty "Articles you save for later show up here.">
|
<!ENTITY home_reading_list_empty "Articles you save for later show up here.">
|
||||||
<!-- Localization note (home_reading_list_hint): The "TIP" string is synonymous to "hint", "clue", etc. This string is displayed
|
<!-- Localization note (home_reading_list_hint): The "TIP" string is synonymous to "hint", "clue", etc. This string is displayed
|
||||||
|
|
|
@ -109,6 +109,9 @@
|
||||||
|
|
||||||
<!ENTITY fxaccount_full_label 'Firefox Accounts'>
|
<!ENTITY fxaccount_full_label 'Firefox Accounts'>
|
||||||
|
|
||||||
|
<!ENTITY fxaccount_custom_server_account_title 'Using account on server'>
|
||||||
|
<!ENTITY fxaccount_custom_server_sync_title 'Storing Sync data on server'>
|
||||||
|
|
||||||
<!-- Localization note: these are shown in all screens that query the
|
<!-- Localization note: these are shown in all screens that query the
|
||||||
user for an email address and password. Hide and show are button
|
user for an email address and password. Hide and show are button
|
||||||
labels. -->
|
labels. -->
|
||||||
|
@ -175,7 +178,9 @@
|
||||||
|
|
||||||
<!ENTITY fxaccount_status_header2 'Firefox Account'>
|
<!ENTITY fxaccount_status_header2 'Firefox Account'>
|
||||||
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
|
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
|
||||||
|
<!ENTITY fxaccount_status_auth_server 'Account server'>
|
||||||
<!ENTITY fxaccount_status_device_name 'Device name'>
|
<!ENTITY fxaccount_status_device_name 'Device name'>
|
||||||
|
<!ENTITY fxaccount_status_sync_server 'Sync server'>
|
||||||
<!ENTITY fxaccount_status_sync '&syncBrand.shortName.label;'>
|
<!ENTITY fxaccount_status_sync '&syncBrand.shortName.label;'>
|
||||||
<!ENTITY fxaccount_status_sync_enabled '&syncBrand.shortName.label;: enabled'>
|
<!ENTITY fxaccount_status_sync_enabled '&syncBrand.shortName.label;: enabled'>
|
||||||
<!ENTITY fxaccount_status_needs_verification2 'Your account needs to be verified. Tap to resend verification email.'>
|
<!ENTITY fxaccount_status_needs_verification2 'Your account needs to be verified. Tap to resend verification email.'>
|
||||||
|
|
|
@ -272,8 +272,6 @@ gbjar.sources += [
|
||||||
'home/HomePagerTabStrip.java',
|
'home/HomePagerTabStrip.java',
|
||||||
'home/HomePanelPicker.java',
|
'home/HomePanelPicker.java',
|
||||||
'home/HomePanelsManager.java',
|
'home/HomePanelsManager.java',
|
||||||
'home/LastTabsPanel.java',
|
|
||||||
'home/MostRecentPanel.java',
|
|
||||||
'home/MultiTypeCursorAdapter.java',
|
'home/MultiTypeCursorAdapter.java',
|
||||||
'home/PanelAuthCache.java',
|
'home/PanelAuthCache.java',
|
||||||
'home/PanelAuthLayout.java',
|
'home/PanelAuthLayout.java',
|
||||||
|
@ -289,6 +287,7 @@ gbjar.sources += [
|
||||||
'home/PinSiteDialog.java',
|
'home/PinSiteDialog.java',
|
||||||
'home/ReadingListPanel.java',
|
'home/ReadingListPanel.java',
|
||||||
'home/ReadingListRow.java',
|
'home/ReadingListRow.java',
|
||||||
|
'home/RecentTabsPanel.java',
|
||||||
'home/SearchEngine.java',
|
'home/SearchEngine.java',
|
||||||
'home/SearchEngineRow.java',
|
'home/SearchEngineRow.java',
|
||||||
'home/SearchLoader.java',
|
'home/SearchLoader.java',
|
||||||
|
|
|
@ -4,15 +4,21 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.preferences;
|
package org.mozilla.gecko.preferences;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.text.Collator;
|
import java.text.Collator;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.mozilla.gecko.BrowserLocaleManager;
|
import org.mozilla.gecko.BrowserLocaleManager;
|
||||||
import org.mozilla.gecko.R;
|
import org.mozilla.gecko.R;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
@ -21,7 +27,51 @@ import android.util.Log;
|
||||||
public class LocaleListPreference extends ListPreference {
|
public class LocaleListPreference extends ListPreference {
|
||||||
private static final String LOG_TAG = "GeckoLocaleList";
|
private static final String LOG_TAG = "GeckoLocaleList";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With thanks to <http://stackoverflow.com/a/22679283/22003> for the
|
||||||
|
* initial solution.
|
||||||
|
*
|
||||||
|
* This class encapsulates an approach to checking whether a script
|
||||||
|
* is usable on a device. We attempt to draw a character from the
|
||||||
|
* script (e.g., ব). If the fonts on the device don't have the correct
|
||||||
|
* glyph, Android typically renders whitespace (rather than .notdef).
|
||||||
|
*
|
||||||
|
* Pass in part of the name of the locale in its local representation,
|
||||||
|
* and a whitespace character; this class performs the graphical comparison.
|
||||||
|
*
|
||||||
|
* See Bug 1023451 Comment 24 for extensive explanation.
|
||||||
|
*/
|
||||||
|
private static class CharacterValidator {
|
||||||
|
private static final int BITMAP_WIDTH = 32;
|
||||||
|
private static final int BITMAP_HEIGHT = 48;
|
||||||
|
|
||||||
|
private final Paint paint = new Paint();
|
||||||
|
private final byte[] missingCharacter;
|
||||||
|
|
||||||
|
public CharacterValidator(String missing) {
|
||||||
|
this.missingCharacter = getPixels(drawBitmap(missing));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap drawBitmap(String text){
|
||||||
|
Bitmap b = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ALPHA_8);
|
||||||
|
Canvas c = new Canvas(b);
|
||||||
|
c.drawText(text, 0, BITMAP_HEIGHT / 2, this.paint);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
private static byte[] getPixels(Bitmap b) {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(b.getByteCount());
|
||||||
|
b.copyPixelsToBuffer(buffer);
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean characterIsMissingInFont(String ch) {
|
||||||
|
byte[] rendered = getPixels(drawBitmap(ch));
|
||||||
|
return Arrays.equals(rendered, missingCharacter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private volatile Locale entriesLocale;
|
private volatile Locale entriesLocale;
|
||||||
|
private final CharacterValidator characterValidator;
|
||||||
|
|
||||||
public LocaleListPreference(Context context) {
|
public LocaleListPreference(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
|
@ -29,6 +79,10 @@ public class LocaleListPreference extends ListPreference {
|
||||||
|
|
||||||
public LocaleListPreference(Context context, AttributeSet attributes) {
|
public LocaleListPreference(Context context, AttributeSet attributes) {
|
||||||
super(context, attributes);
|
super(context, attributes);
|
||||||
|
|
||||||
|
// Thus far, missing glyphs are replaced by whitespace, not a box
|
||||||
|
// or other Unicode codepoint.
|
||||||
|
this.characterValidator = new CharacterValidator(" ");
|
||||||
buildList();
|
buildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,9 +140,54 @@ public class LocaleListPreference extends ListPreference {
|
||||||
// We sort by name, so we use Collator.
|
// We sort by name, so we use Collator.
|
||||||
return COLLATOR.compare(this.nativeName, another.nativeName);
|
return COLLATOR.compare(this.nativeName, another.nativeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See Bug 1023451 Comment 10 for the research that led to
|
||||||
|
* this method.
|
||||||
|
*
|
||||||
|
* @return true if this locale can be used for displaying UI
|
||||||
|
* on this device without known issues.
|
||||||
|
*/
|
||||||
|
public boolean isUsable(CharacterValidator validator) {
|
||||||
|
// Oh, for Java 7 switch statements.
|
||||||
|
if (this.tag.equals("bn-IN")) {
|
||||||
|
// Bengali sometimes has an English label if the Bengali script
|
||||||
|
// is missing. This prevents us from simply checking character
|
||||||
|
// rendering for bn-IN; we'll get a false positive for "B", not "ব".
|
||||||
|
//
|
||||||
|
// This doesn't seem to affect other Bengali-script locales
|
||||||
|
// (below), which always have a label in native script.
|
||||||
|
if (!this.nativeName.startsWith("বাংলা")) {
|
||||||
|
// We're on an Android version that doesn't even have
|
||||||
|
// characters to say বাংলা. Definite failure.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These locales use a script that is often unavailable
|
||||||
|
// on common Android devices. Make sure we can show them.
|
||||||
|
// See documentation for CharacterValidator.
|
||||||
|
// Note that bn-IN is checked here even if it passed above.
|
||||||
|
if (this.tag.equals("or") ||
|
||||||
|
this.tag.equals("pa-IN") ||
|
||||||
|
this.tag.equals("gu-IN") ||
|
||||||
|
this.tag.equals("bn-IN")) {
|
||||||
|
if (validator.characterIsMissingInFont(this.nativeName.substring(0, 1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocaleDescriptor[] getShippingLocales() {
|
/**
|
||||||
|
* Not every locale we ship can be used on every device, due to
|
||||||
|
* font or rendering constraints.
|
||||||
|
*
|
||||||
|
* This method filters down the list before generating the descriptor array.
|
||||||
|
*/
|
||||||
|
private LocaleDescriptor[] getUsableLocales() {
|
||||||
Collection<String> shippingLocales = BrowserLocaleManager.getPackagedLocaleTags(getContext());
|
Collection<String> shippingLocales = BrowserLocaleManager.getPackagedLocaleTags(getContext());
|
||||||
|
|
||||||
// Future: single-locale builds should be specified, too.
|
// Future: single-locale builds should be specified, too.
|
||||||
|
@ -97,15 +196,22 @@ public class LocaleListPreference extends ListPreference {
|
||||||
return new LocaleDescriptor[] { new LocaleDescriptor(fallbackTag) };
|
return new LocaleDescriptor[] { new LocaleDescriptor(fallbackTag) };
|
||||||
}
|
}
|
||||||
|
|
||||||
final int count = shippingLocales.size();
|
final int initialCount = shippingLocales.size();
|
||||||
final LocaleDescriptor[] descriptors = new LocaleDescriptor[count];
|
final Set<LocaleDescriptor> locales = new HashSet<LocaleDescriptor>(initialCount);
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (String tag : shippingLocales) {
|
for (String tag : shippingLocales) {
|
||||||
descriptors[i++] = new LocaleDescriptor(tag);
|
final LocaleDescriptor descriptor = new LocaleDescriptor(tag);
|
||||||
|
|
||||||
|
if (!descriptor.isUsable(this.characterValidator)) {
|
||||||
|
Log.w(LOG_TAG, "Skipping locale " + tag + " on this device.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
locales.add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Arrays.sort(descriptors, 0, count);
|
final int usableCount = locales.size();
|
||||||
|
final LocaleDescriptor[] descriptors = locales.toArray(new LocaleDescriptor[usableCount]);
|
||||||
|
Arrays.sort(descriptors, 0, usableCount);
|
||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +260,7 @@ public class LocaleListPreference extends ListPreference {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocaleDescriptor[] descriptors = getShippingLocales();
|
final LocaleDescriptor[] descriptors = getUsableLocales();
|
||||||
final int count = descriptors.length;
|
final int count = descriptors.length;
|
||||||
|
|
||||||
this.entriesLocale = currentLocale;
|
this.entriesLocale = currentLocale;
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@android:color/transparent"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_light"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_light"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
</selector>
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@android:color/transparent"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_light"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_light"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
</selector>
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@android:color/transparent"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@drawable/history_tabs_indicator_selected"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="false"
|
|
||||||
android:drawable="@drawable/history_tabs_indicator_selected"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="false"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="false"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
<item android:state_focused="true"
|
|
||||||
android:state_selected="true"
|
|
||||||
android:state_pressed="true"
|
|
||||||
android:drawable="@color/background_normal"/>
|
|
||||||
|
|
||||||
</selector>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<org.mozilla.gecko.widget.IconTabWidget android:id="@+id/tab_icon_widget"
|
|
||||||
style="@style/Widget.Home.HistoryTabWidget"
|
|
||||||
android:layout_width="@dimen/history_tab_widget_width"
|
|
||||||
android:layout_height="@dimen/history_tab_widget_height"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout="@layout/home_history_tabs_indicator"
|
|
||||||
gecko:display="text"/>
|
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/history_panel_container"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
style="@style/Widget.Home.HistoryTabIndicator"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/history_tab_indicator_height"
|
|
||||||
android:background="@drawable/home_history_tabs_indicator"/>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<org.mozilla.gecko.widget.IconTabWidget android:id="@+id/tab_icon_widget"
|
|
||||||
style="@style/Widget.Home.HistoryTabWidget"
|
|
||||||
android:layout_width="@dimen/history_tab_widget_width"
|
|
||||||
android:layout_height="@dimen/history_tab_widget_height"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout="@layout/home_history_tabs_indicator"
|
|
||||||
gecko:display="text"/>
|
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/history_panel_container"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
style="@style/Widget.Home.HistoryTabIndicator"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/history_tab_indicator_height"
|
|
||||||
android:background="@drawable/home_history_tabs_indicator"/>
|
|
|
@ -5,8 +5,8 @@
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
-->
|
-->
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout style="@style/FxAccountMiddle" >
|
<LinearLayout style="@style/FxAccountMiddle" >
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
-->
|
-->
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout style="@style/FxAccountMiddle" >
|
<LinearLayout style="@style/FxAccountMiddle" >
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -20,6 +20,8 @@
|
||||||
style="@style/FxAccountHeaderItem"
|
style="@style/FxAccountHeaderItem"
|
||||||
android:text="@string/fxaccount_create_account_header" />
|
android:text="@string/fxaccount_create_account_header" />
|
||||||
|
|
||||||
|
<include layout="@layout/fxaccount_custom_server_view" />
|
||||||
|
|
||||||
<include layout="@layout/fxaccount_email_password_view" />
|
<include layout="@layout/fxaccount_email_password_view" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -89,4 +91,4 @@
|
||||||
android:contentDescription="@string/fxaccount_empty_contentDescription" />
|
android:contentDescription="@string/fxaccount_empty_contentDescription" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
-->
|
-->
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/account_server_layout"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:background="@color/fxaccount_error_preference_backgroundcolor"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/fxaccount_empty_contentDescription"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="48dip"
|
||||||
|
android:padding="10dip"
|
||||||
|
android:src="@drawable/fxaccount_sync_error" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="6dip"
|
||||||
|
android:paddingRight="10dip"
|
||||||
|
android:paddingTop="6dip" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+android:id/account_server_title"
|
||||||
|
style="@style/FxAccountTextItem"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/fxaccount_custom_server_account_title" >
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+android:id/account_server_summary"
|
||||||
|
style="@style/FxAccountTextItem"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:ellipsize="middle"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/sync_server_layout"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:background="@color/fxaccount_error_preference_backgroundcolor"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/fxaccount_empty_contentDescription"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="48dip"
|
||||||
|
android:padding="10dip"
|
||||||
|
android:src="@drawable/fxaccount_sync_error" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="6dip"
|
||||||
|
android:paddingRight="10dip"
|
||||||
|
android:paddingTop="6dip" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+android:id/sync_server_title"
|
||||||
|
style="@style/FxAccountTextItem"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="@string/fxaccount_custom_server_sync_title" >
|
||||||
|
</TextView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+android:id/sync_server_summary"
|
||||||
|
style="@style/FxAccountTextItem"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</merge>
|
|
@ -8,7 +8,7 @@
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical" >
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
@ -25,14 +25,14 @@
|
||||||
</AutoCompleteTextView>
|
</AutoCompleteTextView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/password"
|
android:id="@+id/password"
|
||||||
style="@style/FxAccountEditItem"
|
style="@style/FxAccountEditItem"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/fxaccount_password_background"
|
android:background="@drawable/fxaccount_password_background"
|
||||||
|
@ -51,22 +51,22 @@
|
||||||
happy. Be thankful there are not three buttons! -->
|
happy. Be thankful there are not three buttons! -->
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_weight="0"
|
android:layout_weight="0"
|
||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/show_password"
|
android:id="@+id/show_password"
|
||||||
style="@style/FxAccountShowHidePasswordButton"
|
style="@style/FxAccountShowHidePasswordButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:text="@string/fxaccount_password_show" >
|
android:text="@string/fxaccount_password_show" >
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style="@style/FxAccountShowHidePasswordButton"
|
style="@style/FxAccountShowHidePasswordButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:text="@string/fxaccount_password_show"
|
android:text="@string/fxaccount_password_show"
|
||||||
android:visibility="invisible" >
|
android:visibility="invisible" >
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
<Button
|
<Button
|
||||||
style="@style/FxAccountShowHidePasswordButton"
|
style="@style/FxAccountShowHidePasswordButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:text="@string/fxaccount_password_hide"
|
android:text="@string/fxaccount_password_hide"
|
||||||
android:visibility="invisible" >
|
android:visibility="invisible" >
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
-->
|
-->
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:background="@android:color/transparent">
|
android:background="@android:color/transparent">
|
||||||
|
|
||||||
<ListView android:id="@android:id/list"
|
<ListView android:id="@android:id/list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="0px"
|
android:layout_height="0px"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingTop="0dip"
|
android:paddingTop="0dip"
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -20,6 +20,8 @@
|
||||||
style="@style/FxAccountHeaderItem"
|
style="@style/FxAccountHeaderItem"
|
||||||
android:text="@string/fxaccount_sign_in_sub_header" />
|
android:text="@string/fxaccount_sign_in_sub_header" />
|
||||||
|
|
||||||
|
<include layout="@layout/fxaccount_custom_server_view" />
|
||||||
|
|
||||||
<include layout="@layout/fxaccount_email_password_view" />
|
<include layout="@layout/fxaccount_email_password_view" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -39,7 +41,7 @@
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/fxaccount_error_preference_backgroundcolor"
|
android:background="@color/fxaccount_error_preference_backgroundcolor"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+android:id/title"
|
android:id="@+android:id/title"
|
||||||
style="@style/FxAccountTextItem"
|
style="@style/FxAccountTextItem"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:gravity="center_vertical" >
|
android:gravity="center_vertical" >
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:fillViewport="true" >
|
android:fillViewport="true" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -20,6 +20,8 @@
|
||||||
style="@style/FxAccountHeaderItem"
|
style="@style/FxAccountHeaderItem"
|
||||||
android:text="@string/fxaccount_update_credentials_header" />
|
android:text="@string/fxaccount_update_credentials_header" />
|
||||||
|
|
||||||
|
<include layout="@layout/fxaccount_custom_server_view" />
|
||||||
|
|
||||||
<include layout="@layout/fxaccount_email_password_view" />
|
<include layout="@layout/fxaccount_email_password_view" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -41,6 +43,7 @@
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/forgot_password_link"
|
android:id="@+id/forgot_password_link"
|
||||||
style="@style/FxAccountLinkifiedItem"
|
style="@style/FxAccountLinkifiedItem"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:text="@string/fxaccount_sign_in_forgot_password" />
|
android:text="@string/fxaccount_sign_in_forgot_password" />
|
||||||
|
|
||||||
<LinearLayout style="@style/FxAccountSpacer" />
|
<LinearLayout style="@style/FxAccountSpacer" />
|
||||||
|
|
|
@ -8,17 +8,18 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<FrameLayout android:id="@+id/history_panel_container"
|
<include layout="@layout/home_history_list"/>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<org.mozilla.gecko.widget.IconTabWidget android:id="@+id/tab_icon_widget"
|
<LinearLayout android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="@dimen/browser_toolbar_height"
|
android:background="@color/home_button_bar_bg">
|
||||||
android:tabStripEnabled="false"
|
|
||||||
android:showDividers="none"
|
<Button android:id="@+id/clear_history_button"
|
||||||
android:background="@color/background_light"
|
style="@style/Widget.Home.ActionButton"
|
||||||
android:layout="@layout/home_history_tabs_indicator"/>
|
android:text="@string/home_clear_history_button"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/home_history_tabs_indicator"/>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<include layout="@layout/home_history_list"/>
|
|
||||||
|
|
||||||
<LinearLayout android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/home_button_bar_bg">
|
|
||||||
|
|
||||||
<Button android:id="@+id/clear_history_button"
|
|
||||||
style="@style/Widget.Home.ActionButton"
|
|
||||||
android:text="@string/home_clear_history_button"
|
|
||||||
android:gravity="center"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -10,16 +10,4 @@
|
||||||
|
|
||||||
<include layout="@layout/home_history_list"/>
|
<include layout="@layout/home_history_list"/>
|
||||||
|
|
||||||
<LinearLayout android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@color/home_button_bar_bg">
|
|
||||||
|
|
||||||
<Button android:id="@+id/open_all_tabs_button"
|
|
||||||
style="@style/Widget.Home.ActionButton"
|
|
||||||
android:text="@string/home_last_tabs_open"
|
|
||||||
android:gravity="center"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
|
|
@ -11,20 +11,20 @@
|
||||||
android:text="@string/sync_title_send_tab" />
|
android:text="@string/sync_title_send_tab" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/SyncSpace" >
|
android:padding="@dimen/SyncSpace" >
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:text="@string/sync_title_send_tab" />
|
android:text="@string/sync_title_send_tab" />
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/uri"
|
android:id="@+id/uri"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:text="@string/sync_title_send_tab" />
|
android:text="@string/sync_title_send_tab" />
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/waiting_content1"
|
android:id="@+id/waiting_content1"
|
||||||
style="@style/Widget.ProgressBar.Horizontal"
|
style="@style/Widget.ProgressBar.Horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:indeterminateOnly="true"
|
android:indeterminateOnly="true"
|
||||||
android:padding="@dimen/SyncSpace" />
|
android:padding="@dimen/SyncSpace" />
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
style="@style/SyncLayout" >
|
style="@style/SyncLayout" >
|
||||||
<WebView android:id="@+id/web_engine"
|
<WebView android:id="@+id/web_engine"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="fill_parent" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<dimen name="history_tab_widget_width">360dp</dimen>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -7,7 +7,7 @@
|
||||||
<!-- Top title bar: a text view with the Sync icon to the left. -->
|
<!-- Top title bar: a text view with the Sync icon to the left. -->
|
||||||
<style name="SyncTop" parent="@android:style/Widget.Holo.ActionBar">
|
<style name="SyncTop" parent="@android:style/Widget.Holo.ActionBar">
|
||||||
<item name="android:textAppearance">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
|
<item name="android:textAppearance">@android:style/TextAppearance.Holo.Widget.ActionBar.Title</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:gravity">center_vertical|left</item>
|
<item name="android:gravity">center_vertical|left</item>
|
||||||
<item name="android:drawableLeft">@drawable/icon</item>
|
<item name="android:drawableLeft">@drawable/icon</item>
|
||||||
|
@ -17,12 +17,12 @@
|
||||||
|
|
||||||
<!-- Bottom bar: a horizontal linear layout with buttons in it. -->
|
<!-- Bottom bar: a horizontal linear layout with buttons in it. -->
|
||||||
<style name="SyncBottom" parent="@android:style/Holo.Light.ButtonBar">
|
<style name="SyncBottom" parent="@android:style/Holo.Light.ButtonBar">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="SyncButton" parent="@android:style/Widget.Holo.Light.Button.Small">
|
<style name="SyncButton" parent="@android:style/Widget.Holo.Light.Button.Small">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
<item name="android:background">?android:attr/selectableItemBackground</item>
|
<item name="android:background">?android:attr/selectableItemBackground</item>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<dimen name="history_tab_widget_width">480dp</dimen>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -11,7 +11,6 @@
|
||||||
<dimen name="tabs_counter_size">26sp</dimen>
|
<dimen name="tabs_counter_size">26sp</dimen>
|
||||||
<dimen name="tabs_panel_indicator_width">60dp</dimen>
|
<dimen name="tabs_panel_indicator_width">60dp</dimen>
|
||||||
<dimen name="tabs_panel_list_padding">8dip</dimen>
|
<dimen name="tabs_panel_list_padding">8dip</dimen>
|
||||||
<dimen name="history_tab_widget_width">270dp</dimen>
|
|
||||||
<dimen name="panel_grid_view_column_width">250dp</dimen>
|
<dimen name="panel_grid_view_column_width">250dp</dimen>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -82,12 +82,6 @@
|
||||||
<dimen name="url_bar_offset_left">32dp</dimen>
|
<dimen name="url_bar_offset_left">32dp</dimen>
|
||||||
<dimen name="history_tab_indicator_height">50dp</dimen>
|
<dimen name="history_tab_indicator_height">50dp</dimen>
|
||||||
|
|
||||||
<!-- We need to maintain height for the tab widget on History Page
|
|
||||||
since android does not add footer/header divider height to its
|
|
||||||
calculation for wrap_content in LinearLayout.
|
|
||||||
50dp * 2 Views + 30dp padding + 4dp dividers-->
|
|
||||||
<dimen name="history_tab_widget_height">134dp</dimen>
|
|
||||||
|
|
||||||
<!-- PageActionButtons dimensions -->
|
<!-- PageActionButtons dimensions -->
|
||||||
<dimen name="page_action_button_width">32dp</dimen>
|
<dimen name="page_action_button_width">32dp</dimen>
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<style name="FxAccountMiddle">
|
<style name="FxAccountMiddle">
|
||||||
<item name="android:background">@android:color/white</item>
|
<item name="android:background">@android:color/white</item>
|
||||||
<item name="android:orientation">vertical</item>
|
<item name="android:orientation">vertical</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
<item name="android:paddingTop">30dp</item>
|
<item name="android:paddingTop">30dp</item>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
|
|
||||||
<style name="FxAccountSpacer">
|
<style name="FxAccountSpacer">
|
||||||
<item name="android:orientation">vertical</item>
|
<item name="android:orientation">vertical</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">0dp</item>
|
<item name="android:layout_height">0dp</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
<style name="FxAccountTextItem" parent="@android:style/TextAppearance.Medium">
|
<style name="FxAccountTextItem" parent="@android:style/TextAppearance.Medium">
|
||||||
<item name="android:textColor">@color/fxaccount_textColor</item>
|
<item name="android:textColor">@color/fxaccount_textColor</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:gravity">center_horizontal</item>
|
<item name="android:gravity">center_horizontal</item>
|
||||||
<item name="android:textSize">16sp</item>
|
<item name="android:textSize">16sp</item>
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
<item name="android:textColor">@drawable/fxaccount_button_color</item>
|
<item name="android:textColor">@drawable/fxaccount_button_color</item>
|
||||||
<item name="android:textSize">24sp</item>
|
<item name="android:textSize">24sp</item>
|
||||||
<item name="android:padding">20dp</item>
|
<item name="android:padding">20dp</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_marginBottom">10dp</item>
|
<item name="android:layout_marginBottom">10dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -81,7 +81,7 @@
|
||||||
<item name="android:focusable">true</item>
|
<item name="android:focusable">true</item>
|
||||||
<item name="android:textColor">@color/fxaccount_linkified_textColor</item>
|
<item name="android:textColor">@color/fxaccount_linkified_textColor</item>
|
||||||
<item name="android:textColorLink">@color/fxaccount_linkified_textColorLink</item>
|
<item name="android:textColorLink">@color/fxaccount_linkified_textColorLink</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:gravity">center</item>
|
<item name="android:gravity">center</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="FxAccountErrorItem">
|
<style name="FxAccountErrorItem">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_marginBottom">10dp</item>
|
<item name="android:layout_marginBottom">10dp</item>
|
||||||
<item name="android:layout_marginTop">10dp</item>
|
<item name="android:layout_marginTop">10dp</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
@ -118,13 +118,13 @@
|
||||||
|
|
||||||
<style name="FxAccountButtonLayout">
|
<style name="FxAccountButtonLayout">
|
||||||
<item name="android:orientation">vertical</item>
|
<item name="android:orientation">vertical</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:background">@drawable/fxaccount_button_background</item>
|
<item name="android:background">@drawable/fxaccount_button_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="FxAccountCheckBox">
|
<style name="FxAccountCheckBox">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_marginBottom">10dp</item>
|
<item name="android:layout_marginBottom">10dp</item>
|
||||||
<item name="android:textColor">@drawable/fxaccount_checkbox_textcolor</item>
|
<item name="android:textColor">@drawable/fxaccount_checkbox_textcolor</item>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<style name="SyncLayout">
|
<style name="SyncLayout">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">match_parent</item>
|
<item name="android:layout_height">fill_parent</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="SyncLayout.Vertical" parent="@style/SyncLayout">
|
<style name="SyncLayout.Vertical" parent="@style/SyncLayout">
|
||||||
<item name="android:orientation">vertical</item>
|
<item name="android:orientation">vertical</item>
|
||||||
|
@ -17,14 +17,14 @@
|
||||||
|
|
||||||
<!-- TextView Styles -->
|
<!-- TextView Styles -->
|
||||||
<style name="SyncTextFrame" parent="@style/TextAppearance">
|
<style name="SyncTextFrame" parent="@style/TextAppearance">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">match_parent</item>
|
<item name="android:layout_height">fill_parent</item>
|
||||||
<item name="android:layout_gravity">center</item>
|
<item name="android:layout_gravity">center</item>
|
||||||
<item name="android:padding">@dimen/SyncSpace</item>
|
<item name="android:padding">@dimen/SyncSpace</item>
|
||||||
<item name="android:orientation">vertical</item>
|
<item name="android:orientation">vertical</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="SyncTextItem" parent="@style/TextAppearance.Medium">
|
<style name="SyncTextItem" parent="@style/TextAppearance.Medium">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:textSize">15dp</item>
|
<item name="android:textSize">15dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
</style>
|
</style>
|
||||||
<!-- EditView Styles -->
|
<!-- EditView Styles -->
|
||||||
<style name="SyncEditItem" parent="@style/Widget.EditText">
|
<style name="SyncEditItem" parent="@style/Widget.EditText">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:singleLine">true</item>
|
<item name="android:singleLine">true</item>
|
||||||
<item name="android:inputType">text|textNoSuggestions</item>
|
<item name="android:inputType">text|textNoSuggestions</item>
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
<!-- Top title bar: a text view with the Sync icon to the left. -->
|
<!-- Top title bar: a text view with the Sync icon to the left. -->
|
||||||
<style name="SyncTop">
|
<style name="SyncTop">
|
||||||
<item name="android:textAppearance">@style/TextAppearance.Large</item>
|
<item name="android:textAppearance">@style/TextAppearance.Large</item>
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:gravity">center_vertical|left</item>
|
<item name="android:gravity">center_vertical|left</item>
|
||||||
<item name="android:drawableLeft">@drawable/icon</item>
|
<item name="android:drawableLeft">@drawable/icon</item>
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
|
|
||||||
<!-- Middle scroller: a scroll view with content in it. -->
|
<!-- Middle scroller: a scroll view with content in it. -->
|
||||||
<style name="SyncMiddle">
|
<style name="SyncMiddle">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">0dp</item>
|
<item name="android:layout_height">0dp</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
<item name="android:padding">@dimen/SyncSpace</item>
|
<item name="android:padding">@dimen/SyncSpace</item>
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
|
|
||||||
<!-- Bottom bar: a horizontal linear layout with buttons in it. -->
|
<!-- Bottom bar: a horizontal linear layout with buttons in it. -->
|
||||||
<style name="SyncBottom">
|
<style name="SyncBottom">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_gravity">center</item>
|
<item name="android:layout_gravity">center</item>
|
||||||
<item name="android:gravity">center</item>
|
<item name="android:gravity">center</item>
|
||||||
|
@ -104,7 +104,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="SyncButton" parent="@style/Widget.Button">
|
<style name="SyncButton" parent="@style/Widget.Button">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_weight">1</item>
|
<item name="android:layout_weight">1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
android:key="email"
|
android:key="email"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/fxaccount_email_hint" />
|
android:title="@string/fxaccount_email_hint" />
|
||||||
|
<Preference
|
||||||
|
android:editable="false"
|
||||||
|
android:key="auth_server"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/fxaccount_status_auth_server" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="sync_category"
|
android:key="sync_category"
|
||||||
|
@ -72,6 +77,13 @@
|
||||||
android:key="device_name"
|
android:key="device_name"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/fxaccount_status_device_name" />
|
android:title="@string/fxaccount_status_device_name" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:editable="false"
|
||||||
|
android:key="sync_server"
|
||||||
|
android:persistent="false"
|
||||||
|
android:title="@string/fxaccount_status_sync_server" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="legal_category"
|
android:key="legal_category"
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
<string name="bookmarks_title">&bookmarks_title;</string>
|
<string name="bookmarks_title">&bookmarks_title;</string>
|
||||||
<string name="history_title">&history_title;</string>
|
<string name="history_title">&history_title;</string>
|
||||||
<string name="reading_list_title">&reading_list_title;</string>
|
<string name="reading_list_title">&reading_list_title;</string>
|
||||||
|
<string name="recent_tabs_title">&recent_tabs_title;</string>
|
||||||
|
|
||||||
<string name="switch_to_tab">&switch_to_tab;</string>
|
<string name="switch_to_tab">&switch_to_tab;</string>
|
||||||
|
|
||||||
|
@ -313,10 +314,10 @@
|
||||||
<string name="home_clear_history_button">&home_clear_history_button;</string>
|
<string name="home_clear_history_button">&home_clear_history_button;</string>
|
||||||
<string name="home_clear_history_confirm">&home_clear_history_confirm;</string>
|
<string name="home_clear_history_confirm">&home_clear_history_confirm;</string>
|
||||||
<string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
|
<string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
|
||||||
|
<string name="home_closed_tabs_title">&home_closed_tabs_title;</string>
|
||||||
<string name="home_last_tabs_title">&home_last_tabs_title;</string>
|
<string name="home_last_tabs_title">&home_last_tabs_title;</string>
|
||||||
<string name="home_last_tabs_open">&home_last_tabs_open;</string>
|
<string name="home_last_tabs_open">&home_last_tabs_open;</string>
|
||||||
<string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
|
<string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
|
||||||
<string name="home_most_recent_title">&home_most_recent_title;</string>
|
|
||||||
<string name="home_most_recent_empty">&home_most_recent_empty;</string>
|
<string name="home_most_recent_empty">&home_most_recent_empty;</string>
|
||||||
<string name="home_reading_list_empty">&home_reading_list_empty;</string>
|
<string name="home_reading_list_empty">&home_reading_list_empty;</string>
|
||||||
<string name="home_reading_list_hint">&home_reading_list_hint2;</string>
|
<string name="home_reading_list_hint">&home_reading_list_hint2;</string>
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче