diff options
26 files changed, 1890 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..1362d039 --- /dev/null +++ b/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := user development + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := PackageInstaller +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 00000000..2cc5b1a0 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.packageinstaller"> + <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> + <uses-permission android:name="android.permission.DELETE_PACKAGES" /> + <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> + <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.READ_OWNER_DATA" /> + <application android:label="@string/app_name"> + <activity android:name=".PackageInstallerActivity"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="content" /> + <data android:scheme="file" /> + <data android:mimeType="application/vnd.android.package-archive" /> + </intent-filter> + </activity> + <activity android:name=".InstallAppConfirmation"> + </activity> + <activity android:name=".InstallAppProgress"> + </activity> + <activity android:name=".InstallAppDone"> + </activity> + <activity android:name=".UninstallerActivity"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + <action android:name="android.intent.action.DELETE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="package" /> + </intent-filter> + </activity> + <activity android:name=".UninstallAppProgress"> + </activity> + <activity android:name=".UninstallAppDone"> + </activity> + </application> +</manifest> diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/MODULE_LICENSE_APACHE2 @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/res/drawable/applications.png b/res/drawable/applications.png Binary files differnew file mode 100644 index 00000000..decf7419 --- /dev/null +++ b/res/drawable/applications.png diff --git a/res/drawable/button_indicator_finish.png b/res/drawable/button_indicator_finish.png Binary files differnew file mode 100755 index 00000000..5a01bcc4 --- /dev/null +++ b/res/drawable/button_indicator_finish.png diff --git a/res/drawable/ic_installer_text_dot.png b/res/drawable/ic_installer_text_dot.png Binary files differnew file mode 100755 index 00000000..bb023794 --- /dev/null +++ b/res/drawable/ic_installer_text_dot.png diff --git a/res/drawable/icon.png b/res/drawable/icon.png Binary files differnew file mode 100644 index 00000000..64e3601c --- /dev/null +++ b/res/drawable/icon.png diff --git a/res/drawable/stat_sample.png b/res/drawable/stat_sample.png Binary files differnew file mode 100644 index 00000000..a2e5ce34 --- /dev/null +++ b/res/drawable/stat_sample.png diff --git a/res/layout/app_details.xml b/res/layout/app_details.xml new file mode 100755 index 00000000..3ea84309 --- /dev/null +++ b/res/layout/app_details.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- +Defines the layout of the application snippet that appears on top of the +installation screens +--> +<!-- The snippet about the application - title, icon, description. --> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/app_snippet" + android:layout_width="fill_parent" + android:layout_height="65dip" + android:background="@color/title_background"> + <ImageView android:id="@+id/app_icon" + android:paddingLeft="6dip" + android:layout_width="54dip" + android:layout_height="48dip" + android:background="@color/transparent" + android:layout_alignParentLeft="true" + android:gravity="center" + android:layout_centerInParent="true" + android:scaleType="centerCrop" /> + <TextView android:id="@+id/app_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textColor="?android:attr/textColorPrimary" + android:shadowColor="@color/shadow" + android:shadowRadius="2" + android:layout_toRightOf="@id/app_icon" + android:singleLine="true" + android:gravity="center" + android:layout_centerInParent="true" + android:paddingRight="6dip" + android:paddingLeft="6dip" + android:ellipsize="end"/> +</RelativeLayout> + diff --git a/res/layout/install_confirm.xml b/res/layout/install_confirm.xml new file mode 100755 index 00000000..97bd364e --- /dev/null +++ b/res/layout/install_confirm.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + + Defines the layout of the splash screen that displays the security + settings required for an application and requests the confirmation of the + user before it is installed. +--> + +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/view_background"> + + <LinearLayout + android:id="@+id/all_details" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <include + layout="@layout/app_details" + android:id="@+id/app_snippet"/> + <!-- Security settings description. --> + <TextView + android:id="@+id/security_settings_desc" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:paddingTop="20dip" + android:paddingLeft="20dip" + android:paddingBottom="20dip"/> + + <LinearLayout + android:id="@+id/security_settings_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"/> + + <!-- OK confirm and cancel buttons. --> + <LinearLayout + android:background="@color/title_background" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="bottom" + android:orientation="horizontal"> + <Button + android:id="@+id/ok_button" + android:text="@string/install" + android:layout_width="150dip" + android:paddingLeft="6dip" + android:layout_gravity="left" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + <!-- Spacer --> + <View + android:id="@+id/buttons_spacer_left" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_weight="0.2" /> + <Button + android:id="@+id/cancel_button" + android:layout_width="150dip" + android:paddingRight="6dip" + android:layout_gravity="right" + android:text="@string/cancel" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + </LinearLayout> + </LinearLayout> +</ScrollView> + diff --git a/res/layout/install_done.xml b/res/layout/install_done.xml new file mode 100755 index 00000000..0031beb7 --- /dev/null +++ b/res/layout/install_done.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <include + layout="@layout/app_details" + android:gravity="top" + android:id="@+id/app_snippet" /> + <TextView + android:id="@+id/center_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="20dip" + android:paddingTop="36dip" + android:drawableLeft="@drawable/button_indicator_finish" + android:layout_below="@id/app_snippet" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="?android:attr/textAppearanceMedium"/> + + <!-- Launch and close buttons. --> + <LinearLayout + android:background="@color/title_background" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:orientation="horizontal"> + <Button + android:id="@+id/launch_button" + android:text="@string/launch" + android:layout_width="150dip" + android:paddingLeft="6dip" + android:layout_gravity="left" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + <!-- Spacer --> + <View + android:id="@+id/buttons_spacer_left" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_weight="0.2" /> + <Button + android:id="@+id/done_button" + android:layout_width="150dip" + android:paddingRight="6dip" + android:layout_gravity="right" + android:text="@string/done" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + </LinearLayout> + +</RelativeLayout> + + diff --git a/res/layout/install_start.xml b/res/layout/install_start.xml new file mode 100755 index 00000000..1ef0ab5d --- /dev/null +++ b/res/layout/install_start.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <include + layout="@layout/app_details" + android:id="@+id/app_snippet"/> +</RelativeLayout> + + diff --git a/res/layout/op_progress.xml b/res/layout/op_progress.xml new file mode 100755 index 00000000..b957fdf7 --- /dev/null +++ b/res/layout/op_progress.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <include + layout="@layout/app_details" + android:id="@+id/app_snippet"/> + <ProgressBar + android:id="@+id/progress_bar" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="24dip" + android:paddingLeft="24dip" + android:paddingRight="24dip" + android:max="100" /> + <TextView + android:id="@+id/center_text" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingLeft="24dip" + android:paddingTop="16dip" + android:text="@string/installing" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary"/> +</LinearLayout> + + diff --git a/res/layout/uninstall_confirm.xml b/res/layout/uninstall_confirm.xml new file mode 100755 index 00000000..62bd1544 --- /dev/null +++ b/res/layout/uninstall_confirm.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + + Defines the layout of the confirmation screen that gets displayed when an + application is about to be uninstalled. Includes ok and cancel buttons + to let the uinstallation continue or abort. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@color/view_background"> + + <!-- The snippet about the application - title, icon, description. --> + <include + layout="@layout/app_details" + android:layout_alignParentTop="true" + android:id="@+id/app_snippet" /> + + <!-- uninstall application confirmation question --> + <TextView + android:id="@+id/uninstall_question" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/uninstall_application_question" + android:textStyle="bold" + android:paddingTop="16dip" + android:paddingLeft="16dip" + android:layout_below="@id/app_snippet" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="?android:attr/textAppearanceMedium" + android:paddingBottom="36dip"/> + + <!-- uninstall application confirmation text --> + <TextView + android:id="@+id/uninstall_confirm_text" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/uninstall_application_text" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_below="@id/uninstall_question" + android:paddingLeft="16dip"/> + <!-- OK confirm and cancel buttons. --> + <LinearLayout + android:background="@color/title_background" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:orientation="horizontal"> + <Button + android:id="@+id/ok_button" + android:text="@string/ok" + android:layout_width="150dip" + android:paddingLeft="6dip" + android:layout_gravity="left" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + <!-- Spacer --> + <View + android:id="@+id/buttons_spacer_left" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_weight="0.2" /> + <Button + android:id="@+id/cancel_button" + android:layout_width="150dip" + android:paddingRight="6dip" + android:layout_gravity="right" + android:text="@string/cancel" + android:layout_weight="0.4" + android:layout_height="wrap_content"/> + </LinearLayout> +</RelativeLayout> diff --git a/res/layout/uninstall_done.xml b/res/layout/uninstall_done.xml new file mode 100755 index 00000000..082d48b5 --- /dev/null +++ b/res/layout/uninstall_done.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <LinearLayout + android:id="@+id/top_box" + android:background="@color/title_background" + android:layout_width="fill_parent" + android:layout_height="65dip" + android:layout_alignParentTop="true"/> + + <TextView + android:id="@+id/center_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:paddingTop="60dip" + android:textColor="?android:attr/textColorSecondary" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_below="@id/top_box"/> + <!-- OK confirm and cancel buttons. --> + <!-- LinearLayout + android:background="@color/title_background" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:orientation="horizontal" --> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:background="@color/title_background" + android:layout_alignParentBottom="true"> + + <Button + android:id="@+id/ok_button" + android:paddingRight="6dip" + android:layout_alignParentRight="true" + android:text="@string/ok" + android:layout_width="150dip" + android:layout_height="wrap_content"/> + </RelativeLayout> +</RelativeLayout> + + diff --git a/res/values/colors.xml b/res/values/colors.xml new file mode 100755 index 00000000..0b3f6cfc --- /dev/null +++ b/res/values/colors.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <color name="view_background">#1c1c1c</color> + <color name="ok_button_background">#0f0</color> + <color name="cancel_button_background">#f00</color> + <color name="title_background">#828282</color> + <color name="shadow">#cc222222</color> + <color name="transparent">#00000000</color> +</resources> + diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 00000000..be224560 --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2007 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">Package Installer</string> + <string name="app_description">Application that installs/uninstalls packages</string> + <string name="install">Install</string> + <string name="done">Done</string> + <!-- TODO REMOVE LATER --> + <string name="security_settings_header">Security permissions</string> + <string name="security_settings_desc">Allow this application to:</string> + <string name="cancel">Cancel</string> + <string name="unknown">Unknown</string> + <string name="install_application">Install application</string> + <string name="installing">Installing\u2026</string> + <string name="install_done">Application installed</string> + <string name="install_failed">Application install unsuccessful</string> + <string name="launch">Launch</string> + <string name="unknown_apps_dlg_title">Install blocked</string> + <string name="unknown_apps_dlg_text">For security, your phone is set to block installation of applications not sourced in Android Market.</string> + <string name="ok">OK</string> + <string name="settings">Settings</string> + <string name="manage_applications">Manage applications</string> + <string name="installation_question">Do you want to install this application?</string> + <string name="dlg_app_replacement_title">Replace application</string> + <string name="dlg_app_replacement_statement">The application you are installing will replace another application.\n\nAll previous user data will be saved.</string> + <string name="out_of_space_dlg_title">Out of space</string> + <string name="out_of_space_dlg_text"><xliff:g id="app_name">%1$s</xliff:g> could not be viewed. Free up some space on your phone and try again.</string> + <!-- strings related to uninstall activity --> + <string name="dlg_ok">OK</string> + <string name="app_not_found_dlg_title">Application not found</string> + <string name="app_not_found_dlg_text"> The application was not +found in the list of installed applications.</string> + <string name="uninstall_application_question">Uninstall application?</string> + <string name="uninstall_application_text">This application will be removed from your phone.</string> + <string name="uninstalling">Uninstalling\u2026</string> + <string name="uninstall_done">Uninstall finished!</string> + <string name="uninstall_failed">Application uninstall not successful</string> + <string name="uninstall_application">Uninstall</string> + <string name="app_security_permission_title"><xliff:g id="app_name">%1$s</xliff:g> - security permissions</string> + +</resources> diff --git a/src/com/android/packageinstaller/InstallAppConfirmation.java b/src/com/android/packageinstaller/InstallAppConfirmation.java new file mode 100755 index 00000000..eebe8b59 --- /dev/null +++ b/src/com/android/packageinstaller/InstallAppConfirmation.java @@ -0,0 +1,109 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import java.util.ArrayList; +import android.widget.AppSecurityPermissions; +import android.app.Activity; +import android.content.pm.PermissionInfo; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * This activity corresponds to a confirmation screen that is displayed when the user tries + * to install an application bundled as an apk file. + * The intent that launches this activity should include the application information object + * of the application(to be installed) and a list of permission strings associated + * with the application. This information is displayed on the screen and installation is either + * continued or canceled based on the user response(click ok or cancel). + */ +public class InstallAppConfirmation extends Activity implements View.OnClickListener { + private final String TAG="InstallAppConfirmation"; + private boolean localLOGV = false; + private Button mOk; + private Button mCancel; + private ApplicationInfo mAppInfo; + private Uri mPkgURI; + private View mContentView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + if(localLOGV) Log.i(TAG, "intent="+intent); + mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); + mPkgURI = intent.getData(); + if(localLOGV) Log.i(TAG, "mAppInfo = "+mAppInfo); + initView(); + } + + public void initView() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + String unknown = getString(R.string.unknown); + //set description + String desc = getString(R.string.security_settings_desc); + if(desc == null) { + desc = unknown; + } + LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mContentView = inflater.inflate(R.layout.install_confirm, null); + setContentView(mContentView); + //initialize views + PackageUtil.initAppSnippet(this, mAppInfo, R.id.app_snippet); + if(desc != null) { + ((TextView)findViewById(R.id.security_settings_desc)).setText(desc); + } + //set permissions + AppSecurityPermissions asp = new AppSecurityPermissions(this); + asp.setSecurityPermissionsView(mPkgURI); + LinearLayout securityList = (LinearLayout) mContentView.findViewById(R.id.security_settings_list); + securityList.addView(asp.getPermissionsView()); + mOk = (Button)findViewById(R.id.ok_button); + mCancel = (Button)findViewById(R.id.cancel_button); + mOk.setOnClickListener(this); + mCancel.setOnClickListener(this); + } + + public void setResultAndReturn(int result) { + if(result == RESULT_CANCELED) Log.i(TAG, "Result has been canceled"); + if(result == RESULT_OK) Log.i(TAG, "result ok"); + setResult(result); + finish(); + } + + public void onClick(View v) { + int result = RESULT_CANCELED; + if(v == mOk) { + result = RESULT_OK; + setResultAndReturn(result); + } else if(v == mCancel) { + result = RESULT_CANCELED; + setResultAndReturn(result); + } + } +} diff --git a/src/com/android/packageinstaller/InstallAppDone.java b/src/com/android/packageinstaller/InstallAppDone.java new file mode 100755 index 00000000..6e21e6e9 --- /dev/null +++ b/src/com/android/packageinstaller/InstallAppDone.java @@ -0,0 +1,90 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.TextView; + +/** + * This activity corresponds to a install status screen that is displayed + * when the user tries + * to install an application bundled as an apk file. The screen + * has two buttons to either launch the newly installed application + * or close the screen. The installation result and the package uri are passed through the + * intent that launches the activity. + */ +public class InstallAppDone extends Activity implements View.OnClickListener { + private final String TAG="InstallAppDone"; + private boolean localLOGV = false; + private ApplicationInfo mAppInfo; + private Button mDoneButton; + private Button mLaunchButton; + private boolean installFlag; + private Intent mLaunchIntent; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); + installFlag = intent.getBooleanExtra(PackageUtil.INTENT_ATTR_INSTALL_STATUS, true); + initView(); + } + + public void initView() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + String unknown = getString(R.string.unknown); + setContentView(R.layout.install_done); + //initialize views + PackageUtil.initAppSnippet(this, mAppInfo, R.id.app_snippet); + TextView centerText = (TextView)findViewById(R.id.center_text); + if(installFlag) { + centerText.setText(getString(R.string.install_done)); + } else { + centerText.setText(R.string.install_failed); + } + mDoneButton = (Button)findViewById(R.id.done_button); + mDoneButton.setOnClickListener(this); + mLaunchButton = (Button)findViewById(R.id.launch_button); + //enable or disable launch buton + mLaunchIntent = PackageUtil.getLaunchIntentForPackage(this, + mAppInfo.packageName); + if(mLaunchIntent != null) { + mLaunchButton.setOnClickListener(this); + } else { + mLaunchButton.setEnabled(false); + } + } + + public void onClick(View v) { + if(v == mDoneButton) { + Log.i(TAG, "Finished installing "+mAppInfo); + finish(); + } else if(v == mLaunchButton) { + startActivity(mLaunchIntent); + finish(); + } + } +} diff --git a/src/com/android/packageinstaller/InstallAppProgress.java b/src/com/android/packageinstaller/InstallAppProgress.java new file mode 100755 index 00000000..c75ba106 --- /dev/null +++ b/src/com/android/packageinstaller/InstallAppProgress.java @@ -0,0 +1,103 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageInstallObserver; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.Window; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * This activity corresponds to a download progress screen that is displayed + * when the user tries + * to install an application bundled as an apk file. The result of the application install + * is indicated in the result code that gets set to the corresponding installation status + * codes defined in PackageManager + */ +public class InstallAppProgress extends Activity { + private final String TAG="InstallAppProgress"; + private boolean localLOGV = false; + private ApplicationInfo mAppInfo; + private Uri mPackageURI; + private ProgressBar mProgressBar; + private final int INSTALL_COMPLETE = 1; + private Handler mHandler = new Handler() { + public static final String TAG = "InstallAppProgress.Handler"; + public void handleMessage(Message msg) { + switch (msg.what) { + case INSTALL_COMPLETE: + //finish the activity posting result + setResultAndFinish(msg.arg1); + break; + default: + break; + } + } + }; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); + mPackageURI = intent.getData(); + initView(); + } + + class PackageInstallObserver extends IPackageInstallObserver.Stub { + public void packageInstalled(String packageName, int returnCode) { + Message msg = mHandler.obtainMessage(INSTALL_COMPLETE); + msg.arg1 = returnCode; + mHandler.sendMessage(msg); + } + } + + void setResultAndFinish(int retCode) { + try { + Log.i(TAG, "Sleeping for 5 seconds to display screen"); + Thread.sleep(5*1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Intent data = new Intent(); + setResult(retCode); + finish(); + } + + public void initView() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + String unknown = getString(R.string.unknown); + setContentView(R.layout.op_progress); + //initialize views + PackageUtil.initAppSnippet(this, mAppInfo, R.id.app_snippet); + TextView installTextView = (TextView)findViewById(R.id.center_text); + installTextView.setText(R.string.installing); + mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); + mProgressBar.setIndeterminate(true); + PackageInstallObserver observer = new PackageInstallObserver(); + getPackageManager().installPackage(mPackageURI, observer, 0); + } +} diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java new file mode 100644 index 00000000..a1be9169 --- /dev/null +++ b/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -0,0 +1,348 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.net.Uri; +import android.os.Bundle; +import android.os.FileUtils; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.provider.Settings; +import android.text.Html; +import android.util.Log; +import android.view.Window; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageParser.Package; + +public class PackageInstallerActivity extends Activity { + private static final int INSTALL_INITIAL=0; + private static final int INSTALL_CONFIRM=1; + private static final int INSTALL_PROGRESS=2; + private static final int INSTALL_DONE=3; + private static final String TAG = "PackageInstaller"; + private Uri mPackageURI; + private boolean localLOGV = true; + private int mCurrentState = INSTALL_INITIAL; + PackageManager mPm; + private Package mPkgInfo; + private File mTmpFile; + private Uri mPackageUri; + private static final int DELETE_COMPLETE=1; + private static final int SUCCEEDED=1; + private static final int FAILED=0; + private static final int FREE_SPACE = 2; + private Handler mHandler = new Handler() { + public static final String TAG = "PackageInstallerActivity.Handler"; + public void handleMessage(Message msg) { + switch (msg.what) { + case DELETE_COMPLETE: + //finish the activity posting result + startInstallConfirm(); + break; + case FREE_SPACE: + if(msg.arg1 == SUCCEEDED) { + makeTempCopyAndInstall(); + } else { + displayOutOfSpaceDialog(); + } + break; + default: + break; + } + } + }; + + private void startInstallActivityClass(int requestCode, Class<?> cls) { + Intent newIntent = new Intent(); + startInstallActivityClass(newIntent, requestCode, cls); + } + private void startInstallActivityClass(Intent newIntent, int requestCode, Class<?> cls) { + newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, + mPkgInfo.applicationInfo); + newIntent.setData(mPackageURI); + newIntent.setClass(this, cls); + if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); + startActivityForResult(newIntent, requestCode); + } + + + private void startInstallConfirm() { + Intent newIntent = new Intent(); + newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, + mPkgInfo.applicationInfo); + newIntent.setData(mPackageURI); + newIntent.setClass(this, InstallAppConfirmation.class); + startActivityForResult(newIntent, INSTALL_CONFIRM); + } + + private void startInstallProgress() { + startInstallActivityClass(INSTALL_PROGRESS, InstallAppProgress.class); + } + + private void startInstallDone(boolean result) { + Intent newIntent = new Intent(Intent.ACTION_VIEW); + newIntent.putExtra(PackageUtil.INTENT_ATTR_INSTALL_STATUS, result); + startInstallActivityClass(newIntent, INSTALL_DONE, InstallAppDone.class); + } + + private void displayReplaceAppDialog() { + new AlertDialog.Builder(this) + .setTitle(R.string.dlg_app_replacement_title) + .setMessage(R.string.dlg_app_replacement_statement) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + replacePackage(); + }}) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Canceling installation"); + finish(); + }}) + .setCancelable(false) + .show(); + } + + + /* + * Utility method to display a dialog prompting the user to turn on settings property + * before installing application + */ + private void displayUnknowAppsDialog() { + new AlertDialog.Builder(this) + .setTitle(R.string.unknown_apps_dlg_title) + .setMessage(R.string.unknown_apps_dlg_text) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Finishing off activity so that user can navigate to settings manually"); + finish(); + }}) + .setPositiveButton(R.string.settings, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Launching settings"); + launchSettingsAppAndFinish(); + }}) + .setCancelable(false) + .show(); + } + + /* + * Utility method to display dialog indicating out of disk space + */ + private void displayOutOfSpaceDialog() { + //guaranteed not to be null. will default to package name if not set by app + CharSequence appTitle = mPm.getApplicationLabel(mPkgInfo.applicationInfo); + String dlgText = getString(R.string.out_of_space_dlg_text, + appTitle.toString()); + + new AlertDialog.Builder(this) + .setTitle(R.string.out_of_space_dlg_title) + .setMessage(dlgText) + .setPositiveButton(R.string.manage_applications, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + //launch manage applications + Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE"); + startActivity(intent); + finish(); + }}) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + Log.i(TAG, "Canceling installation"); + finish(); + }}) + .setCancelable(false) + .show(); + } + + private class PkgDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) + throws RemoteException { + Message msg = mHandler.obtainMessage(FREE_SPACE); + msg.arg1 = succeeded?SUCCEEDED:FAILED; + mHandler.sendMessage(msg); + } + + } + + private void checkOutOfSpace(long size) { + if(localLOGV) Log.i(TAG, "Checking for "+size+" number of bytes"); + PkgDataObserver observer = new PkgDataObserver(); + mPm.freeApplicationCache(size, observer); + } + + private void launchSettingsAppAndFinish() { + //Create an intent to launch SettingsTwo activity + Intent launchSettingsIntent = new Intent(Settings.ACTION_APPLICATION_SETTINGS); + startActivity(launchSettingsIntent); + finish(); + } + + private boolean isInstallingUnknownAppsAllowed() { + return Settings.System.getInt(getContentResolver(), + Settings.System.INSTALL_NON_MARKET_APPS, 0) > 0; + } + + private File createTempPackageFile(String filePath) { + File tmpPackageFile; + int i = filePath.lastIndexOf("/"); + String tmpFileName; + if(i != -1) { + tmpFileName = filePath.substring(i+1); + } else { + tmpFileName = filePath; + } + FileOutputStream fos; + try { + fos=openFileOutput(tmpFileName, MODE_WORLD_READABLE); + } catch (FileNotFoundException e1) { + Log.e(TAG, "Error opening file "+tmpFileName); + return null; + } + try { + fos.close(); + } catch (IOException e) { + Log.e(TAG, "Error opening file "+tmpFileName); + return null; + } + tmpPackageFile=getFileStreamPath(tmpFileName); + File srcPackageFile = new File(filePath); + if (!FileUtils.copyFile(srcPackageFile, tmpPackageFile)) { + return null; + } + return tmpPackageFile; + } + + private void makeTempCopyAndInstall() { + //copy file to tmp dir + mTmpFile = createTempPackageFile(mPackageURI.getPath()); + if(mTmpFile == null) { + //display a dialog + Log.e(TAG, "Error copying file locally. Failed Installation"); + displayOutOfSpaceDialog(); + return; + } + mPackageURI = Uri.parse("file://"+mTmpFile.getPath()); + //check out of space condition. display dialog if necessary + if(PackageUtil.isPackageAlreadyInstalled(this, mPkgInfo.applicationInfo.packageName)) { + displayReplaceAppDialog(); + } else { + startInstallConfirm(); + } + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + //get intent information + final Intent intent = getIntent(); + mPackageURI = intent.getData(); + mPkgInfo = PackageUtil.getPackageInfo(mPackageURI); + if(mPkgInfo == null) { + Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation"); + finish(); + return; + } + mPm = getPackageManager(); + //set view + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.install_start); + PackageUtil.initAppSnippet(this, mPkgInfo.applicationInfo, R.id.app_snippet); + //check setting + if(!isInstallingUnknownAppsAllowed()) { + //ask user to enable setting first + displayUnknowAppsDialog(); + return; + } + //compute the size of the application. just an estimate + long size; + String apkPath = mPackageURI.getPath(); + File apkFile = new File(apkPath); + //TODO? DEVISE BETTER HEAURISTIC + size = 4*apkFile.length(); + checkOutOfSpace(size); + } + + class PackageDeleteObserver extends IPackageDeleteObserver.Stub { + public void packageDeleted(boolean succeeded) throws RemoteException { + Message msg = mHandler.obtainMessage(DELETE_COMPLETE); + msg.arg1 = succeeded?SUCCEEDED:FAILED; + mHandler.sendMessage(msg); + } + } + + + void replacePackage() { + PackageDeleteObserver observer = new PackageDeleteObserver(); + mPm.deletePackage(mPkgInfo.applicationInfo.packageName, observer, + PackageManager.DONT_DELETE_DATA); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + boolean finish = true; + switch(requestCode) { + case INSTALL_CONFIRM: + if(resultCode == RESULT_OK) { + finish = false; + mCurrentState = INSTALL_CONFIRM; + startInstallProgress(); + } + break; + case INSTALL_PROGRESS: + boolean ok = false; + finish = false; + mCurrentState = INSTALL_DONE; + if(resultCode == PackageManager.INSTALL_SUCCEEDED) { + ok = true; + } + //start the next screen to show final status of installation + startInstallDone(ok); + break; + case INSTALL_DONE: + //neednt check for result code here + break; + default: + break; + } + if(finish) { + if(mTmpFile != null) { + deleteFile(mTmpFile.getName()); + } + //finish off this activity to return to the previous activity that launched it + Log.i(TAG, "Finishing off activity"); + finish(); + } + } +} diff --git a/src/com/android/packageinstaller/PackageUtil.java b/src/com/android/packageinstaller/PackageUtil.java new file mode 100644 index 00000000..f7819a48 --- /dev/null +++ b/src/com/android/packageinstaller/PackageUtil.java @@ -0,0 +1,150 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import java.io.File; +import java.util.List; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageParser.Package; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * This is a utility class for defining some utility methods and constants + * used in the package installer application. + */ +public class PackageUtil { + public static final String PREFIX="com.android.packageinstaller."; + public static final String INTENT_ATTR_INSTALL_STATUS = PREFIX+"installStatus"; + public static final String INTENT_ATTR_APPLICATION_INFO=PREFIX+"applicationInfo"; + public static final String INTENT_ATTR_PERMISSIONS_LIST=PREFIX+"PermissionsList"; + //intent attribute strings related to uninstall + public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName"; + + /* + * Utility method to get application information for a given packageURI + */ + public static ApplicationInfo getApplicationInfo(Uri packageURI) { + final String archiveFilePath = packageURI.getPath(); + PackageParser packageParser = new PackageParser(archiveFilePath); + File sourceFile = new File(archiveFilePath); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0); + if (pkg == null) { + return null; + } + return pkg.applicationInfo; + } + + /* + * Utility method to get package information for a given packageURI + */ + public static Package getPackageInfo(Uri packageURI) { + final String archiveFilePath = packageURI.getPath(); + PackageParser packageParser = new PackageParser(archiveFilePath); + File sourceFile = new File(archiveFilePath); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0); + } + + /* + * Utility method to get application label from package manager for a given context + */ + public static CharSequence getApplicationLabel(Context context, ApplicationInfo appInfo) { + CharSequence appName = context.getPackageManager().getApplicationLabel(appInfo); + if(appName == null) { + appName = context.getString(R.string.unknown); + } + return appName; + } + + /* + * Utility method to getApplicationIcon from package manager for a given context + */ + public static Drawable getApplicationIcon(Context context, ApplicationInfo appInfo) { + return context.getPackageManager().getApplicationIcon(appInfo); + } + + /* + * Utility method to display application snippet. make sure to setContentView on context + * before invoking this method + */ + public static View initAppSnippet(Activity context, ApplicationInfo appInfo, int snippetId) { + View appSnippet = context.findViewById(snippetId); + ((ImageView)appSnippet.findViewById(R.id.app_icon)).setImageDrawable( + getApplicationIcon(context, appInfo)); + ((TextView)appSnippet.findViewById(R.id.app_name)).setText( + getApplicationLabel(context, appInfo)); + return appSnippet; + } + + public static boolean isPackageAlreadyInstalled(Activity context, String pkgName) { + List<PackageInfo> installedList = context.getPackageManager().getInstalledPackages(0); + int installedListSize = installedList.size(); + for(int i = 0; i < installedListSize; i++) { + PackageInfo tmp = installedList.get(i); + if(pkgName.equalsIgnoreCase(tmp.packageName)) { + return true; + } + + } + return false; + } + + /** + * Returns an intent that can be used to launch the main activity in the given package. + * + * @param ctx + * @param packageName + * @return an intent launching the main activity in the given package + */ + public static Intent getLaunchIntentForPackage(Context ctx, String packageName) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PackageManager manager = ctx.getPackageManager(); + Intent intentToResolve = new Intent(Intent.ACTION_MAIN, null); + intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); + final List<ResolveInfo> apps = + manager.queryIntentActivities(intentToResolve, 0); + // TODO in future add a new tag to application for launchable main activity + for (ResolveInfo app : apps) { + if (app.activityInfo.packageName.equals(packageName)) { + intent.setClassName(packageName, app.activityInfo.name); + return intent; + } + } + return null; + } +} diff --git a/src/com/android/packageinstaller/UninstallAppDone.java b/src/com/android/packageinstaller/UninstallAppDone.java new file mode 100755 index 00000000..73dffe3d --- /dev/null +++ b/src/com/android/packageinstaller/UninstallAppDone.java @@ -0,0 +1,72 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.TextView; + +/** + * This activity corresponds to a uninstall status screen that is displayed + * when an application gets uninstalled. The screen contains a single ok button at the + * bottom. + */ +public class UninstallAppDone extends Activity implements View.OnClickListener { + private final String TAG="UninstallAppDone"; + private boolean localLOGV = false; + private ApplicationInfo mAppInfo; + private Button mOkButton; + private boolean uninstallFlag; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); + //TODO set installFlag + uninstallFlag = intent.getBooleanExtra(PackageUtil.INTENT_ATTR_INSTALL_STATUS, true); + initView(); + } + + public void initView() { + String unknown = getString(R.string.unknown); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.uninstall_done); + TextView centerText = (TextView)findViewById(R.id.center_text); + if(uninstallFlag) { + centerText.setText(getString(R.string.uninstall_done)); + } else { + centerText.setText(R.string.uninstall_failed); + } + mOkButton = (Button)findViewById(R.id.ok_button); + mOkButton.setOnClickListener(this); + } + + public void onClick(View v) { + if(v == mOkButton) { + Log.i(TAG, "Finished installing "+mAppInfo); + finish(); + } + } +} diff --git a/src/com/android/packageinstaller/UninstallAppProgress.java b/src/com/android/packageinstaller/UninstallAppProgress.java new file mode 100755 index 00000000..44a9802e --- /dev/null +++ b/src/com/android/packageinstaller/UninstallAppProgress.java @@ -0,0 +1,102 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDeleteObserver; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.Window; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * This activity corresponds to a download progress screen that is displayed + * when an application is uninstalled. The result of the application uninstall + * is indicated in the result code that gets set to 0 or 1. The application gets launched + * by an intent with the intent's class name explicitly set to UninstallAppProgress and expects + * the application object of the application to uninstall. + */ +public class UninstallAppProgress extends Activity { + private final String TAG="UninstallAppProgress"; + private boolean localLOGV = false; + private ApplicationInfo mAppInfo; + private ProgressBar mProgressBar; + private final int UNINSTALL_COMPLETE = 1; + public final static int SUCCEEDED=1; + public final static int FAILED=0; + private Handler mHandler = new Handler() { + public static final String TAG = "UninstallAppProgress.Handler"; + public void handleMessage(Message msg) { + switch (msg.what) { + case UNINSTALL_COMPLETE: + //finish the activity posting result + setResultAndFinish(msg.arg1); + break; + default: + break; + } + } + }; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Intent intent = getIntent(); + mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); + initView(); + } + + class PackageDeleteObserver extends IPackageDeleteObserver.Stub { + public void packageDeleted(boolean succeeded) { + Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE); + msg.arg1 = succeeded?SUCCEEDED:FAILED; + mHandler.sendMessage(msg); + } + }; + + void setResultAndFinish(int retCode) { + try { + Log.i(TAG, "Sleeping for some time to display screen"); + Thread.sleep(5*1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Intent data = new Intent(); + setResult(retCode); + finish(); + } + + public void initView() { + String unknown = getString(R.string.unknown); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.op_progress); + //initialize views + PackageUtil.initAppSnippet(this, mAppInfo, R.id.app_snippet); + TextView installTextView = (TextView)findViewById(R.id.center_text); + installTextView.setText(R.string.uninstalling); + mProgressBar = (ProgressBar) findViewById(R.id.progress_bar); + mProgressBar.setIndeterminate(true); + PackageDeleteObserver observer = new PackageDeleteObserver(); + getPackageManager().deletePackage(mAppInfo.packageName, observer, 0); + } +} diff --git a/src/com/android/packageinstaller/UninstallerActivity.java b/src/com/android/packageinstaller/UninstallerActivity.java new file mode 100755 index 00000000..fa1c7afc --- /dev/null +++ b/src/com/android/packageinstaller/UninstallerActivity.java @@ -0,0 +1,158 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package com.android.packageinstaller; + +import com.android.packageinstaller.R; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.content.pm.PackageManager.NameNotFoundException; + +/* + * This activity presents UI to uninstall an application. Usually launched with intent + * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute + * com.android.packageinstaller.PackageName set to the application package name + */ +public class UninstallerActivity extends Activity implements OnClickListener { + private static final String TAG = "UninstallerActivity"; + private boolean localLOGV = false; + //states indicating status of ui display when uninstalling application + private static final int UNINSTALL_CONFIRM=1; + private static final int UNINSTALL_PROGRESS=2; + private static final int UNINSTALL_DONE=3; + private int mCurrentState = UNINSTALL_CONFIRM; + PackageManager mPm; + private ApplicationInfo mAppInfo; + private Button mOk; + private Button mCancel; + + private void startUninstallProgress() { + Intent newIntent = new Intent(Intent.ACTION_VIEW); + newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, + mAppInfo); + newIntent.setClass(this, UninstallAppProgress.class); + startActivityForResult(newIntent, UNINSTALL_PROGRESS); + } + + private void startUninstallDone(boolean result) { + Intent newIntent = new Intent(Intent.ACTION_VIEW); + newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, + mAppInfo); + newIntent.putExtra(PackageUtil.INTENT_ATTR_INSTALL_STATUS, result); + newIntent.setClass(this, UninstallAppDone.class); + startActivityForResult(newIntent, UNINSTALL_DONE); + } + + private void displayErrorDialog(int msgId) { + //display confirmation dialog + new AlertDialog.Builder(this) + .setTitle(getString(R.string.app_not_found_dlg_title)) + .setIcon(com.android.internal.R.drawable.ic_dialog_alert) + .setMessage(getString(msgId)) + .setNeutralButton(getString(R.string.dlg_ok), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + //force to recompute changed value + finish(); + } + } + ) + .show(); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + //get intent information + final Intent intent = getIntent(); + Uri packageURI = intent.getData(); + String packageName = packageURI.getEncodedSchemeSpecificPart(); + if(packageName == null) { + Log.e(TAG, "Invalid package name:"+packageName); + displayErrorDialog(R.string.app_not_found_dlg_text); + return; + } + //initialize package manager + mPm = getPackageManager(); + boolean errFlag = false; + try { + mAppInfo = mPm.getApplicationInfo(packageName, 0); + } catch (NameNotFoundException e) { + errFlag = true; + } + if(mAppInfo == null || errFlag) { + Log.e(TAG, "Invalid application:"+packageName); + displayErrorDialog(R.string.app_not_found_dlg_text); + } else { + requestWindowFeature(Window.FEATURE_NO_TITLE); + //set view + setContentView(R.layout.uninstall_confirm); + PackageUtil.initAppSnippet(this, mAppInfo, R.id.app_snippet); + //initialize ui elements + mOk = (Button)findViewById(R.id.ok_button); + mCancel = (Button)findViewById(R.id.cancel_button); + mOk.setOnClickListener(this); + mCancel.setOnClickListener(this); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + boolean finish = true; + switch(requestCode) { + case UNINSTALL_PROGRESS: + finish = false; + mCurrentState = UNINSTALL_DONE; + //start the next screen to show final status of installation + startUninstallDone(resultCode==UninstallAppProgress.SUCCEEDED); + break; + case UNINSTALL_DONE: + //neednt check for result code here + break; + default: + break; + } + if(finish) { + //finish off this activity to return to the previous activity that launched it + Log.i(TAG, "Finishing off activity"); + finish(); + } + } + + private void finishAndReturn() { + finish(); + } + + public void onClick(View v) { + if(v == mOk) { + //initiate next screen + startUninstallProgress(); + } else if(v == mCancel) { + finishAndReturn(); + } + } +} |