Make package installer a sequence of alert-dialogs.

To make sure the dialog does not change height a single content for all
steps of the sequence. We just unhide the view that should actually be
shown.

Also added a night-mode theme.

Test: Manually uninstalled, installed and update package.
      atest CtsNoPermissionTestCases
            CtsNoPermissionTestCases25
	    CtsPackageInstallTestCases
	    CtsPackageUninstallTestCases
	    CtsPackageInstallerTapjackingTestCases
Change-Id: I890bb1f2697df3af87b6cb65e460f611334523ee
This commit is contained in:
Philip P. Moltmann
2018-08-02 12:12:55 -07:00
parent bcd72a50b7
commit d25ec877da
18 changed files with 310 additions and 730 deletions

View File

@@ -19,7 +19,7 @@
android:label="@string/app_name"
android:icon="@drawable/ic_app_icon"
android:allowBackup="false"
android:theme="@style/DialogWhenLarge"
android:theme="@style/Theme.AlertDialogActivity"
android:supportsRtl="true"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true">
@@ -32,6 +32,7 @@
</receiver>
<activity android:name=".InstallStart"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter android:priority="1">
@@ -59,13 +60,14 @@
android:exported="false" />
<activity android:name=".DeleteStagedFileOnResult"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
android:exported="false" />
<activity android:name=".InstallInstalling"
android:theme="@style/DialogWhenLargeNoAnimation"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
<receiver android:name=".InstallEventReceiver"
@@ -77,18 +79,17 @@
</receiver>
<activity android:name=".InstallSuccess"
android:theme="@style/DialogWhenLargeNoAnimation"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
<activity android:name=".InstallFailed"
android:theme="@style/DialogWhenLargeNoAnimation"
android:theme="@style/Theme.AlertDialogActivity.NoAnimation"
android:exported="false" />
<activity android:name=".UninstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/AlertDialogActivity">
android:noHistory="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.DELETE" />
<action android:name="android.intent.action.UNINSTALL_PACKAGE" />
@@ -107,7 +108,6 @@
<activity android:name=".UninstallUninstalling"
android:excludeFromRecents="true"
android:theme="@style/AlertDialogActivity"
android:exported="false" />
<receiver android:name=".UninstallFinish"

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/app_snippet"
android:background="?android:attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:orientation="horizontal"
android:elevation="@dimen/headerElevation"
android:gravity="center_vertical">
<ImageView android:id="@+id/app_icon"
android:layout_marginStart="16dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_file_download" />
<TextView android:id="@+id/app_name"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/titleTextStyle"
android:singleLine="true"
android:text="@string/app_name_unknown"
android:ellipsize="end" />
</LinearLayout>
<ScrollView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="16dip">
<TextView android:id="@+id/install_confirm_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />
</ScrollView>
<LinearLayout style="?android:attr/buttonBarStyle"
android:background="?android:attr/colorBackground"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:measureWithLargestChild="true">
<!-- spacer to push buttons to the right -->
<View android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button android:id="@+id/cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle" />
<Button android:id="@+id/ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/install"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?android:attr/alertDialogTheme"
android:paddingTop="8dp"
android:paddingLeft="?android:attr/dialogPreferredPadding"
android:paddingRight="?android:attr/dialogPreferredPadding">
<LinearLayout
android:id="@+id/staging"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="invisible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/message_staging" />
<ProgressBar
android:id="@+id/progress_indeterminate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
style="?android:attr/progressBarStyleHorizontal"
android:indeterminate="true" />
</LinearLayout>
<LinearLayout
android:id="@+id/installing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="invisible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/installing" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>
<TextView
android:id="@+id/install_confirm_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_confirm_question"
android:visibility="invisible" />
<TextView
android:id="@+id/install_confirm_question_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_confirm_question_update"
android:visibility="invisible" />
<TextView
android:id="@+id/install_confirm_question_update_system"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_confirm_question_update_system"
android:visibility="invisible" />
<TextView
android:id="@+id/install_success"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_done"
android:visibility="invisible" />
<TextView
android:id="@+id/install_failed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_failed"
android:visibility="invisible" />
<TextView
android:id="@+id/install_failed_blocked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_failed_blocked"
android:visibility="invisible" />
<TextView
android:id="@+id/install_failed_conflict"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_failed_conflict"
android:visibility="invisible" />
<TextView
android:id="@+id/install_failed_incompatible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_failed_incompatible"
android:visibility="invisible" />
<TextView
android:id="@+id/install_failed_invalid_apk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@android:style/TextAppearance.Material.Subhead"
android:text="@string/install_failed_invalid_apk"
android:visibility="invisible" />
</FrameLayout>

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/app_snippet"
android:background="?android:attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:orientation="horizontal"
android:elevation="@dimen/headerElevation"
android:gravity="center_vertical">
<ImageView android:id="@+id/app_icon"
android:layout_marginStart="16dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="fitCenter" />
<TextView android:id="@+id/app_name"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/titleTextStyle"
android:singleLine="true"
android:ellipsize="end" />
</LinearLayout>
<LinearLayout android:id="@+id/simple_status_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="16dip"
android:paddingRight="16dip">
<ImageView android:id="@+id/center_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:src="@drawable/ic_report_problem_92"
android:tint="@color/bigIconColor"
android:contentDescription="@null" />
<TextView android:id="@+id/simple_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout android:id="@+id/buttons_panel"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:measureWithLargestChild="true"
android:padding="8dip">
<!-- spacer to push button to the right -->
<View android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button android:id="@+id/done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/done"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,108 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/app_snippet"
android:background="?android:attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:orientation="horizontal"
android:elevation="@dimen/headerElevation"
android:gravity="center_vertical">
<ImageView
android:id="@+id/app_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="?android:attr/titleTextStyle" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="16dip"
android:paddingRight="16dip">
<ImageView
android:layout_width="92dp"
android:layout_height="92dp"
android:layout_marginBottom="12dp"
android:contentDescription="@null"
android:tint="@color/bigIconColor"
android:src="@drawable/ic_file_download" />
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:indeterminate="false" />
<TextView
android:id="@+id/center_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/installing"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout
android:id="@+id/buttons_panel"
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:measureWithLargestChild="true"
android:orientation="horizontal"
android:padding="8dip">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button
android:id="@+id/cancel_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/cancel" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,113 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- title bar -->
<LinearLayout android:id="@+id/app_snippet"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/colorPrimary"
android:elevation="@dimen/headerElevation"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginLeft="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_file_download"
android:tint="?android:attr/colorAccent" />
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginRight="16dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/app_name_unknown"
android:textAppearance="?android:attr/titleTextStyle" />
</LinearLayout>
<!-- content -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="16dip"
android:paddingRight="16dip">
<ImageView
android:layout_width="92dp"
android:layout_height="92dp"
android:scaleType="fitCenter"
android:layout_marginBottom="12dp"
android:contentDescription="@null"
android:tint="@color/bigIconColor"
android:src="@drawable/ic_file_download" />
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:indeterminate="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/message_staging"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<!-- Bottom buttons -->
<LinearLayout style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<!-- spacer to push button to the right -->
<View android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button android:id="@+id/cancel_button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/cancel" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,101 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout android:id="@+id/app_snippet"
android:background="?android:attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:orientation="horizontal"
android:elevation="@dimen/headerElevation"
android:gravity="center_vertical">
<ImageView android:id="@+id/app_icon"
android:layout_marginStart="16dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:scaleType="fitCenter" />
<TextView android:id="@+id/app_name"
android:layout_marginStart="32dp"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/titleTextStyle"
android:singleLine="true"
android:ellipsize="end" />
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingLeft="16dip"
android:paddingRight="16dip">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:src="@drawable/ic_done_92"
android:tint="@color/bigIconColor"
android:contentDescription="@null" />
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/install_done"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:measureWithLargestChild="true"
android:padding="8dip">
<!-- spacer to push buttons to the right -->
<View android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<Button android:id="@+id/done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/done"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle" />
<Button android:id="@+id/launch_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/launch"
android:maxLines="2"
style="?android:attr/buttonBarButtonStyle" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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>
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Dialog.Alert" />
</resources>

View File

@@ -36,11 +36,11 @@
<!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update">Do you want to install an update
to this existing application? Your existing data will not
be lost. The updated application will get access to:</string>
be lost.</string>
<!-- Message for updating an existing system app [CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update_system">Do you want to install an update
to this built-in application? Your existing data will not
be lost. The updated application will get access to:</string>
be lost.</string>
<!-- [CHAR LIMIT=100] -->
<string name="install_failed">App not installed.</string>
<!-- Reason displayed when installation fails because the package was blocked

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="MediumText"
parent="@android:style/TextAppearance.Medium">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="SmallText"
parent="@android:style/TextAppearance.Small">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TitleText">
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textSize">20dp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="ActionBar" parent="@android:style/Widget.Material.ActionBar.Solid">
<item name="android:contentInsetStart">72dp</item>
</style>
</resources>

View File

@@ -17,25 +17,11 @@
<resources>
<style name="DialogWhenLarge"
parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
<item name="android:textAppearanceMedium">@style/MediumText</item>
<item name="android:textAppearanceSmall">@style/SmallText</item>
<item name="android:titleTextStyle">@style/TitleText</item>
</style>
<style name="DialogWhenLargeNoAnimation" parent="DialogWhenLarge">
<style name="Theme.AlertDialogActivity.NoAnimation">
<item name="android:windowAnimationStyle">@null</item>
</style>
<style name="AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Light.Panel">
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Header.Settings"
parent="@android:style/Theme.DeviceDefault.Settings">
</style>
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
</resources>

View File

@@ -30,41 +30,49 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.view.View;
import com.android.internal.app.AlertActivity;
import java.io.File;
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
public class InstallFailed extends Activity {
public class InstallFailed extends AlertActivity {
private static final String LOG_TAG = InstallFailed.class.getSimpleName();
/** Label of the app that failed to install */
private CharSequence mLabel;
/**
* Convert an package installer status code into the user friendly label.
* Unhide the appropriate label for the statusCode.
*
* @param statusCode The status code from the package installer.
*
* @return The user friendly label for the status code
*/
private int getExplanationFromErrorCode(int statusCode) {
private void setExplanationFromErrorCode(int statusCode) {
Log.d(LOG_TAG, "Installation status code: " + statusCode);
View viewToEnable;
switch (statusCode) {
case PackageInstaller.STATUS_FAILURE_BLOCKED:
return R.string.install_failed_blocked;
viewToEnable = requireViewById(R.id.install_failed_blocked);
break;
case PackageInstaller.STATUS_FAILURE_CONFLICT:
return R.string.install_failed_conflict;
viewToEnable = requireViewById(R.id.install_failed_conflict);
break;
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
return R.string.install_failed_incompatible;
viewToEnable = requireViewById(R.id.install_failed_incompatible);
break;
case PackageInstaller.STATUS_FAILURE_INVALID:
return R.string.install_failed_invalid_apk;
viewToEnable = requireViewById(R.id.install_failed_invalid_apk);
break;
default:
return R.string.install_failed;
viewToEnable = requireViewById(R.id.install_failed);
break;
}
viewToEnable.setVisibility(View.VISIBLE);
}
@Override
@@ -89,8 +97,6 @@ public class InstallFailed extends Activity {
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
Uri packageURI = intent.getData();
setContentView(R.layout.install_failed);
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
@@ -106,7 +112,12 @@ public class InstallFailed extends Activity {
// Store label for dialog
mLabel = as.label;
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.done),
(ignored, ignored2) -> finish(), null);
setupAlert();
// Show out of space dialog if needed
if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
@@ -114,11 +125,7 @@ public class InstallFailed extends Activity {
}
// Get status messages
((TextView) findViewById(R.id.simple_status)).setText(
getExplanationFromErrorCode(statusCode));
// Set up "done" button
findViewById(R.id.done_button).setOnClickListener(view -> finish());
setExplanationFromErrorCode(statusCode);
}
}

View File

@@ -19,8 +19,8 @@ package com.android.packageinstaller;
import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
@@ -30,9 +30,11 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import com.android.internal.app.AlertActivity;
import com.android.internal.content.PackageHelper;
import java.io.File;
@@ -47,7 +49,7 @@ import java.io.OutputStream;
* <p>This has two phases: First send the data to the package manager, then wait until the package
* manager processed the result.</p>
*/
public class InstallInstalling extends Activity {
public class InstallInstalling extends AlertActivity {
private static final String LOG_TAG = InstallInstalling.class.getSimpleName();
private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
@@ -78,11 +80,31 @@ public class InstallInstalling extends Activity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_installing);
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}
if (mSessionId > 0) {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}
setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);
if ("package".equals(mPackageURI.getScheme())) {
try {
@@ -92,10 +114,6 @@ public class InstallInstalling extends Activity {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
sourceFile), R.id.app_snippet);
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
@@ -153,21 +171,7 @@ public class InstallInstalling extends Activity {
}
}
mCancelButton = (Button) findViewById(R.id.cancel_button);
mCancelButton.setOnClickListener(view -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}
if (mSessionId > 0) {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}
setResult(RESULT_CANCELED);
finish();
});
mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
mSessionCallback = new InstallSessionCallback();
}
@@ -307,7 +311,7 @@ public class InstallInstalling extends Activity {
@Override
public void onProgressChanged(int sessionId, float progress) {
if (sessionId == mSessionId) {
ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar);
ProgressBar progressBar = requireViewById(R.id.progress);
progressBar.setMax(Integer.MAX_VALUE);
progressBar.setProgress((int) (Integer.MAX_VALUE * progress));
}

View File

@@ -29,6 +29,9 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.android.internal.app.AlertActivity;
import java.io.File;
import java.io.FileOutputStream;
@@ -40,7 +43,7 @@ import java.io.OutputStream;
* If a package gets installed from an content URI this step loads the package and turns it into
* and installation from a file. Then it re-starts the installation as usual.
*/
public class InstallStaging extends Activity {
public class InstallStaging extends AlertActivity {
private static final String LOG_TAG = InstallStaging.class.getSimpleName();
private static final String STAGED_FILE = "STAGED_FILE";
@@ -55,7 +58,19 @@ public class InstallStaging extends Activity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_staging);
mAlert.setIcon(R.drawable.ic_file_download);
mAlert.setTitle(getString(R.string.app_name_unknown));
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mStagingTask != null) {
mStagingTask.cancel(true);
}
setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.staging).setVisibility(View.VISIBLE);
if (savedInstanceState != null) {
mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));
@@ -64,14 +79,6 @@ public class InstallStaging extends Activity {
mStagedFile = null;
}
}
findViewById(R.id.cancel_button).setOnClickListener(view -> {
if (mStagingTask != null) {
mStagingTask.cancel(true);
}
setResult(RESULT_CANCELED);
finish();
});
}
@Override

View File

@@ -19,6 +19,7 @@ package com.android.packageinstaller;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -26,15 +27,18 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.android.internal.app.AlertActivity;
import java.io.File;
import java.util.List;
/**
* Finish installation: Return status code to the caller or display "success" UI to user
*/
public class InstallSuccess extends Activity {
public class InstallSuccess extends AlertActivity {
private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
@Override
@@ -53,8 +57,6 @@ public class InstallSuccess extends Activity {
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
Uri packageURI = intent.getData();
setContentView(R.layout.install_success);
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
@@ -67,16 +69,20 @@ public class InstallSuccess extends Activity {
as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
}
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
// Set up "done" button
findViewById(R.id.done_button).setOnClickListener(view -> {
if (appInfo.packageName != null) {
Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
}
finish();
});
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
(ignored, ignored2) -> {
if (appInfo.packageName != null) {
Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
}
finish();
}, null);
setupAlert();
requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
// Enable or disable "launch" button
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(
appInfo.packageName);
@@ -89,7 +95,7 @@ public class InstallSuccess extends Activity {
}
}
Button launchButton = (Button)findViewById(R.id.launch_button);
Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
if (enabled) {
launchButton.setOnClickListener(view -> {
try {

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2018 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 static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.app.Activity;
import android.os.Bundle;
class OverlayTouchActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(savedInstanceState);
}
}

View File

@@ -16,6 +16,8 @@
*/
package com.android.packageinstaller;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.StringRes;
@@ -45,9 +47,9 @@ import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import com.android.internal.app.AlertActivity;
import java.io.File;
@@ -61,7 +63,7 @@ import java.io.File;
* Based on the user response the package is then installed by launching InstallAppConfirm
* sub activity. All state transitions are handled in this activity
*/
public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {
public class PackageInstallerActivity extends AlertActivity {
private static final String TAG = "PackageInstaller";
private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
@@ -95,7 +97,6 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
// Buttons to indicate user acceptance
private Button mOk;
private Button mCancel;
private PackageUtil.AppSnippet mAppSnippet;
@@ -119,18 +120,18 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
private boolean mEnableOk = false;
private void startInstallConfirm() {
int msg;
View viewToEnable;
if (mAppInfo != null) {
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
viewToEnable = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? requireViewById(R.id.install_confirm_question_update_system)
: requireViewById(R.id.install_confirm_question_update);
} else {
// This is a new application with no permissions.
msg = R.string.install_confirm_question;
viewToEnable = requireViewById(R.id.install_confirm_question);
}
((TextView) findViewById(R.id.install_confirm_question)).setText(msg);
viewToEnable.setVisibility(View.VISIBLE);
mEnableOk = true;
mOk.setEnabled(true);
@@ -280,6 +281,8 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
@Override
protected void onCreate(Bundle icicle) {
getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
if (icicle != null) {
@@ -344,7 +347,7 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi(R.layout.install_confirm);
bindUi();
checkIfAllowedAndInitiateInstall();
}
@@ -374,17 +377,34 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources);
}
private void bindUi(int layout) {
setContentView(layout);
mOk = (Button) findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
private void bindUi() {
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall();
}
}
}, null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
PackageUtil.initSnippetForNewApp(this, mAppSnippet, R.id.app_snippet);
}
/**
@@ -525,26 +545,6 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On
super.onBackPressed();
}
public void onClick(View v) {
if (v == mOk) {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall();
}
}
} else if (v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}
}
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();

View File

@@ -108,26 +108,6 @@ public class PackageUtil {
icon);
}
/**
* Utility method to display application snippet of a new package.
* The content view should have been set on context before invoking this method.
* appSnippet view should include R.id.app_icon and R.id.app_name
* defined on it.
*
* @param pContext context of package that can load the resources
* @param as The resources to be loaded
* @param snippetId view id of app snippet view
*/
@NonNull public static View initSnippetForNewApp(@NonNull Activity pContext,
@NonNull AppSnippet as, int snippetId) {
View appSnippet = pContext.findViewById(snippetId);
if (as.icon != null) {
((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(as.icon);
}
((TextView)appSnippet.findViewById(R.id.app_name)).setText(as.label);
return appSnippet;
}
static public class AppSnippet {
@NonNull public CharSequence label;
@Nullable public Drawable icon;