summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 14:00:00 (GMT)
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 14:00:00 (GMT)
commit088073e6787fca4bfc68d6b04a5e887a03faf745 (patch)
tree47ae03b80214e7b82791cac1aade88a718e3643e
downloadandroid_packages_apps_PackageInstaller-088073e6787fca4bfc68d6b04a5e887a03faf745.zip
android_packages_apps_PackageInstaller-088073e6787fca4bfc68d6b04a5e887a03faf745.tar.gz
android_packages_apps_PackageInstaller-088073e6787fca4bfc68d6b04a5e887a03faf745.tar.bz2
Initial Contribution
-rw-r--r--Android.mk11
-rw-r--r--AndroidManifest.xml39
-rw-r--r--MODULE_LICENSE_APACHE20
-rw-r--r--NOTICE190
-rw-r--r--res/drawable/applications.pngbin0 -> 2238 bytes
-rwxr-xr-xres/drawable/button_indicator_finish.pngbin0 -> 617 bytes
-rwxr-xr-xres/drawable/ic_installer_text_dot.pngbin0 -> 332 bytes
-rw-r--r--res/drawable/icon.pngbin0 -> 6094 bytes
-rw-r--r--res/drawable/stat_sample.pngbin0 -> 1088 bytes
-rwxr-xr-xres/layout/app_details.xml52
-rwxr-xr-xres/layout/install_confirm.xml89
-rwxr-xr-xres/layout/install_done.xml71
-rwxr-xr-xres/layout/install_start.xml27
-rwxr-xr-xres/layout/op_progress.xml46
-rwxr-xr-xres/layout/uninstall_confirm.xml91
-rwxr-xr-xres/layout/uninstall_done.xml62
-rwxr-xr-xres/values/colors.xml25
-rw-r--r--res/values/strings.xml55
-rwxr-xr-xsrc/com/android/packageinstaller/InstallAppConfirmation.java109
-rwxr-xr-xsrc/com/android/packageinstaller/InstallAppDone.java90
-rwxr-xr-xsrc/com/android/packageinstaller/InstallAppProgress.java103
-rw-r--r--src/com/android/packageinstaller/PackageInstallerActivity.java348
-rw-r--r--src/com/android/packageinstaller/PackageUtil.java150
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallAppDone.java72
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallAppProgress.java102
-rwxr-xr-xsrc/com/android/packageinstaller/UninstallerActivity.java158
26 files changed, 1890 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..1362d03
--- /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 0000000..2cc5b1a
--- /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 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -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
new file mode 100644
index 0000000..decf741
--- /dev/null
+++ b/res/drawable/applications.png
Binary files differ
diff --git a/res/drawable/button_indicator_finish.png b/res/drawable/button_indicator_finish.png
new file mode 100755
index 0000000..5a01bcc
--- /dev/null
+++ b/res/drawable/button_indicator_finish.png
Binary files differ
diff --git a/res/drawable/ic_installer_text_dot.png b/res/drawable/ic_installer_text_dot.png
new file mode 100755
index 0000000..bb02379
--- /dev/null
+++ b/res/drawable/ic_installer_text_dot.png
Binary files differ
diff --git a/res/drawable/icon.png b/res/drawable/icon.png
new file mode 100644
index 0000000..64e3601
--- /dev/null
+++ b/res/drawable/icon.png
Binary files differ
diff --git a/res/drawable/stat_sample.png b/res/drawable/stat_sample.png
new file mode 100644
index 0000000..a2e5ce3
--- /dev/null
+++ b/res/drawable/stat_sample.png
Binary files differ
diff --git a/res/layout/app_details.xml b/res/layout/app_details.xml
new file mode 100755
index 0000000..3ea8430
--- /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 0000000..97bd364
--- /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 0000000..0031beb
--- /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 0000000..1ef0ab5
--- /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 0000000..b957fdf
--- /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 0000000..62bd154
--- /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 0000000..082d48b
--- /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 0000000..0b3f6cf
--- /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 0000000..be22456
--- /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 0000000..eebe8b5
--- /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 0000000..6e21e6e
--- /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 0000000..c75ba10
--- /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 0000000..a1be916
--- /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 0000000..f7819a4
--- /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 0000000..73dffe3
--- /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 0000000..44a9802
--- /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 0000000..fa1c7af
--- /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();
+ }
+ }
+}