Copying out AlertController and its relevant resources
To remove dependency of PackageInstaller package on the internal
AlertController, we are copying out all classes, style, drawables
etc from the internal/ directory into packageinstaller/, to make the
AlertController functional as is
Subsequent change will remove the resources not required by the
PackageInstaller in order to make the AlertController work.
Test: Manual UI testing.
Bug: 241139604
Change-Id: Ifdb46c37379911a40b5bcf34c13f0c7be35abcfe
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_bright.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_bright.9.png
new file mode 100644
index 0000000..6e5fbb5
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_dark.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_dark.9.png
new file mode 100644
index 0000000..3434b2d
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_medium.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_medium.9.png
new file mode 100644
index 0000000..673a509
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_center_bright.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_center_bright.9.png
new file mode 100644
index 0000000..c2a739c
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_center_bright.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_center_dark.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_center_dark.9.png
new file mode 100644
index 0000000..9d2bfb1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_center_dark.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_center_medium.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_center_medium.9.png
new file mode 100644
index 0000000..4375bf2d
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_center_medium.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_full_bright.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_full_bright.9.png
new file mode 100644
index 0000000..6b8aa9d
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_full_bright.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_full_dark.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_full_dark.9.png
new file mode 100644
index 0000000..2884abe
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_full_dark.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_top_bright.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_top_bright.9.png
new file mode 100644
index 0000000..76c35ec
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_top_bright.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/popup_top_dark.9.png b/packages/PackageInstaller/res/drawable-hdpi/popup_top_dark.9.png
new file mode 100644
index 0000000..f317330
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-hdpi/popup_top_dark.9.png
Binary files differ
diff --git a/packages/PackageInstaller/res/drawable/ic_dialog_info.png b/packages/PackageInstaller/res/drawable/ic_dialog_info.png
new file mode 100644
index 0000000..efee1ef
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_dialog_info.png
Binary files differ
diff --git a/packages/PackageInstaller/res/layout-television/alert_dialog_button_bar_leanback.xml b/packages/PackageInstaller/res/layout-television/alert_dialog_button_bar_leanback.xml
new file mode 100644
index 0000000..3ced1db
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/alert_dialog_button_bar_leanback.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?android:attr/buttonBarStyle">
+ <com.android.packageinstaller.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:gravity="start">
+
+ <Button
+ android:id="@+id/button1"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/button2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <Button
+ android:id="@+id/button3"
+ style="?attr/buttonBarNeutralButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </com.android.packageinstaller.ButtonBarLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/res/layout-television/alert_dialog_leanback.xml b/packages/PackageInstaller/res/layout-television/alert_dialog_leanback.xml
new file mode 100644
index 0000000..0290624
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/alert_dialog_leanback.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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.
+-->
+
+<com.android.packageinstaller.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:orientation="vertical">
+
+ <include layout="@layout/alert_dialog_title_material" />
+
+ <FrameLayout
+ android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <Space
+ android:id="@+id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <FrameLayout
+ android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_button_bar_leanback" />
+</com.android.packageinstaller.AlertDialogLayout>
diff --git a/packages/PackageInstaller/res/layout-television/alert_dialog_leanback_button_panel_side.xml b/packages/PackageInstaller/res/layout-television/alert_dialog_leanback_button_panel_side.xml
new file mode 100644
index 0000000..f9668dd
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/alert_dialog_leanback_button_panel_side.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/leftPanel"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="16dip"
+ android:paddingBottom="8dip">
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginEnd="8dip"
+ android:scaleType="fitCenter"
+ android:src="@null" />
+ <com.android.packageinstaller.DialogTitle android:id="@+id/alertTitle"
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
+ </LinearLayout>
+ <!-- If the client uses a customTitle, it will be added here. -->
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:minHeight="64dp">
+ <ScrollView android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="16dip"
+ android:paddingBottom="16dip" />
+ </ScrollView>
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/buttonPanel"
+ style="?attr/buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="vertical"
+ android:gravity="end">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="vertical">
+ <Button android:id="@+id/button3"
+ style="?attr/buttonBarNeutralButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ <Button android:id="@+id/button2"
+ style="?attr/buttonBarNegativeButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ <Button android:id="@+id/button1"
+ style="?attr/buttonBarPositiveButtonStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog.xml b/packages/PackageInstaller/res/layout/alert_dialog.xml
new file mode 100644
index 0000000..16b6d39
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog.xml
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/alert_dialog.xml
+**
+** Copyright 2006, 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:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="9dip"
+ android:paddingBottom="3dip"
+ android:paddingStart="3dip"
+ android:paddingEnd="1dip">
+
+ <LinearLayout android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="54dip"
+ android:orientation="vertical">
+ <LinearLayout android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="9dip"
+ android:layout_marginStart="10dip"
+ android:layout_marginEnd="10dip">
+ <ImageView android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingTop="6dip"
+ android:paddingEnd="10dip"
+ android:src="@drawable/ic_dialog_info" />
+ <com.android.packageinstaller.DialogTitle android:id="@+id/alertTitle"
+ style="?android:attr/textAppearanceLarge"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
+ </LinearLayout>
+ <ImageView android:id="@+id/titleDivider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:visibility="gone"
+ android:scaleType="fitXY"
+ android:gravity="fill_horizontal"
+ android:src="@android:drawable/divider_horizontal_dark" />
+ <!-- If the client uses a customTitle, it will be added here. -->
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <ScrollView android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="2dip"
+ android:paddingBottom="12dip"
+ android:paddingStart="14dip"
+ android:paddingEnd="10dip"
+ android:overScrollMode="ifContentScrolls">
+ <TextView android:id="@+id/message"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="5dip" />
+ </ScrollView>
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <FrameLayout android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip" />
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="54dip"
+ android:orientation="vertical" >
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="4dip"
+ android:paddingStart="2dip"
+ android:paddingEnd="2dip"
+ android:measureWithLargestChild="true">
+ <LinearLayout android:id="@+id/leftSpacer"
+ android:layout_weight="0.25"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone" />
+ <Button android:id="@+id/button1"
+ android:layout_width="0dip"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button3"
+ android:layout_width="0dip"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarNeutralButtonStyle"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button2"
+ android:layout_width="0dip"
+ android:layout_gravity="end"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:maxLines="2"
+ android:layout_height="wrap_content" />
+ <LinearLayout android:id="@+id/rightSpacer"
+ android:layout_width="0dip"
+ android:layout_weight="0.25"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone" />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_button_bar_material.xml b/packages/PackageInstaller/res/layout/alert_dialog_button_bar_material.xml
new file mode 100644
index 0000000..e4977e7
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_button_bar_material.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarAlwaysDrawVerticalTrack="true"
+ android:scrollIndicators="top|bottom"
+ android:fillViewport="true"
+ style="?android:attr/buttonBarStyle">
+ <com.android.packageinstaller.ButtonBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale"
+ android:orientation="horizontal"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:gravity="bottom">
+
+ <Button
+ android:id="@+id/button3"
+ style="?android:attr/buttonBarNeutralButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:visibility="invisible" />
+
+ <Button
+ android:id="@+id/button2"
+ style="?android:attr/buttonBarNegativeButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/button1"
+ style="?android:attr/buttonBarPositiveButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </com.android.packageinstaller.ButtonBarLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_holo.xml b/packages/PackageInstaller/res/layout/alert_dialog_holo.xml
new file mode 100644
index 0000000..b5b3927
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_holo.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dip"
+ android:layout_marginEnd="8dip"
+ android:orientation="vertical">
+
+ <LinearLayout android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <View android:id="@+id/titleDividerTop"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:visibility="gone"
+ android:background="@android:color/holo_blue_light" />
+ <LinearLayout android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start"
+ android:minHeight="@dimen/alert_dialog_title_height"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip">
+ <ImageView android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingEnd="8dip"
+ android:src="@null" />
+ <com.android.packageinstaller.DialogTitle android:id="@+id/alertTitle"
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
+ </LinearLayout>
+ <View android:id="@+id/titleDivider"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:visibility="gone"
+ android:background="@android:color/holo_blue_light" />
+ <!-- If the client uses a customTitle, it will be added here. -->
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:minHeight="64dp">
+ <ScrollView android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
+ </ScrollView>
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="0dip">
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layoutDirection="locale"
+ android:measureWithLargestChild="true">
+ <Button android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_gravity="start"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:layout_height="wrap_content" />
+ <Button android:id="@+id/button1"
+ android:layout_width="wrap_content"
+ android:layout_gravity="end"
+ android:layout_weight="1"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_material.xml b/packages/PackageInstaller/res/layout/alert_dialog_material.xml
new file mode 100644
index 0000000..10e9149
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_material.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<com.android.packageinstaller.AlertDialogLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="start|top"
+ android:orientation="vertical">
+
+ <include layout="@layout/alert_dialog_title_material" />
+
+ <FrameLayout
+ android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Space
+ android:id="@+id/textSpacerNoTitle"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ <Space
+ android:id="@+id/textSpacerNoButtons"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_padding_top_material" />
+ </LinearLayout>
+ </ScrollView>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp">
+
+ <FrameLayout
+ android:id="@+id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_button_bar_material" />
+</com.android.packageinstaller.AlertDialogLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_progress.xml b/packages/PackageInstaller/res/layout/alert_dialog_progress.xml
new file mode 100644
index 0000000..fe06b65
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_progress.xml
@@ -0,0 +1,48 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content" android:layout_height="match_parent">
+ <ProgressBar android:id="@+id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dip"
+ android:layout_marginBottom="1dip"
+ android:layout_marginStart="10dip"
+ android:layout_marginEnd="10dip"
+ android:layout_centerHorizontal="true" />
+ <TextView
+ android:id="@+id/progress_percent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dip"
+ android:layout_marginStart="10dip"
+ android:layout_marginEnd="10dip"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/progress"
+ />
+ <TextView
+ android:id="@+id/progress_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="12dip"
+ android:layout_marginStart="10dip"
+ android:layout_marginEnd="10dip"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/progress"
+ />
+</RelativeLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_progress_material.xml b/packages/PackageInstaller/res/layout/alert_dialog_progress_material.xml
new file mode 100644
index 0000000..fb98259
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_progress_material.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingTop="@dimen/dialog_padding_top_material"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingBottom="@dimen/dialog_padding_top_material">
+ <ProgressBar
+ android:id="@+id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true" />
+ <TextView
+ android:id="@+id/progress_percent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/progress" />
+ <TextView
+ android:id="@+id/progress_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/progress" />
+</RelativeLayout>
diff --git a/packages/PackageInstaller/res/layout/alert_dialog_title_material.xml b/packages/PackageInstaller/res/layout/alert_dialog_title_material.xml
new file mode 100644
index 0000000..45d9bb6
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/alert_dialog_title_material.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- If the client uses a customTitle, it will be added here. -->
+
+ <LinearLayout
+ android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingTop="@dimen/dialog_padding_top_material">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginEnd="8dip"
+ android:scaleType="fitCenter"
+ android:src="@null" />
+
+ <com.android.packageinstaller.DialogTitle
+ android:id="@+id/alertTitle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ style="?android:attr/windowTitleStyle" />
+ </LinearLayout>
+
+ <Space
+ android:id="@+id/titleDividerNoCustom"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialog_title_divider_material" />
+</LinearLayout>
diff --git a/packages/PackageInstaller/res/layout/progress_dialog.xml b/packages/PackageInstaller/res/layout/progress_dialog.xml
new file mode 100644
index 0000000..0d3cd4a
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/progress_dialog.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/alert_dialog.xml
+**
+** Copyright 2006, 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">
+
+ <LinearLayout android:id="@+id/body"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:paddingStart="8dip"
+ android:paddingTop="10dip"
+ android:paddingEnd="8dip"
+ android:paddingBottom="10dip">
+
+ <ProgressBar android:id="@android:id/progress"
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:max="10000"
+ android:layout_marginEnd="12dip" />
+
+ <TextView android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/PackageInstaller/res/layout/progress_dialog_material.xml b/packages/PackageInstaller/res/layout/progress_dialog_material.xml
new file mode 100644
index 0000000..2417965
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/progress_dialog_material.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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">
+
+ <LinearLayout
+ android:id="@+id/body"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingTop="@dimen/dialog_padding_top_material"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingBottom="@dimen/dialog_padding_top_material">
+
+ <ProgressBar
+ android:id="@id/progress"
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:max="10000"
+ android:layout_marginEnd="?attr/dialogPreferredPadding" />
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/packages/PackageInstaller/res/layout/select_dialog.xml b/packages/PackageInstaller/res/layout/select_dialog.xml
new file mode 100644
index 0000000..e2d0739
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/select_dialog.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!--
+ This layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the ListView to display the items.
+ Assign an ID so its state will be saved/restored.
+-->
+<view class="com.android.packageinstaller.AlertController$RecycleListView"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/select_dialog_listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5px"
+ android:cacheColorHint="@null"
+ android:divider="?android:attr/listDividerAlertDialog"
+ android:scrollbars="vertical"
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart" />
diff --git a/packages/PackageInstaller/res/layout/select_dialog_item.xml b/packages/PackageInstaller/res/layout/select_dialog_item.xml
new file mode 100644
index 0000000..835c433
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_item.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/layout/select_dialog_item.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!--
+ This layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the TextView to display individual
+ items.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="14dip"
+ android:paddingEnd="15dip"
+ android:ellipsize="marquee"
+/>
diff --git a/packages/PackageInstaller/res/layout/select_dialog_item_material.xml b/packages/PackageInstaller/res/layout/select_dialog_item_material.xml
new file mode 100644
index 0000000..b45edc6
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_item_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<!--
+ This layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the TextView to display individual
+ items.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:ellipsize="marquee" />
diff --git a/packages/PackageInstaller/res/layout/select_dialog_material.xml b/packages/PackageInstaller/res/layout/select_dialog_material.xml
new file mode 100644
index 0000000..125b9b8
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_material.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<!--
+ This layout file is used by the AlertDialog when displaying a list of items.
+ This layout file is inflated and used as the ListView to display the items.
+ Assign an ID so its state will be saved/restored.
+-->
+<view class="com.android.packageinstaller.AlertController$RecycleListView"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/select_dialog_listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:cacheColorHint="@null"
+ android:divider="?attr/listDividerAlertDialog"
+ android:scrollbars="vertical"
+ android:overScrollMode="ifContentScrolls"
+ android:textAlignment="viewStart"
+ android:clipToPadding="false"/>
+ <!--android:paddingBottomNoButtons="@dimen/dialog_list_padding_bottom_no_buttons"
+ android:paddingTopNoTitle="@dimen/dialog_list_padding_top_no_title"/>-->
diff --git a/packages/PackageInstaller/res/layout/select_dialog_multichoice.xml b/packages/PackageInstaller/res/layout/select_dialog_multichoice.xml
new file mode 100644
index 0000000..2dd83a3
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_multichoice.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="12dip"
+ android:paddingEnd="7dip"
+ android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ android:ellipsize="marquee"
+/>
+
diff --git a/packages/PackageInstaller/res/layout/select_dialog_multichoice_material.xml b/packages/PackageInstaller/res/layout/select_dialog_multichoice_material.xml
new file mode 100644
index 0000000..52f709e
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_multichoice_material.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/select_dialog_padding_start_material"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:drawableStart="?android:attr/listChoiceIndicatorMultiple"
+ android:drawablePadding="@dimen/select_dialog_drawable_padding_start_material"
+ android:ellipsize="marquee" />
diff --git a/packages/PackageInstaller/res/layout/select_dialog_singlechoice.xml b/packages/PackageInstaller/res/layout/select_dialog_singlechoice.xml
new file mode 100644
index 0000000..1e18b44
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_singlechoice.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="12dip"
+ android:paddingEnd="7dip"
+ android:checkMark="?android:attr/listChoiceIndicatorSingle"
+ android:ellipsize="marquee"
+/>
diff --git a/packages/PackageInstaller/res/layout/select_dialog_singlechoice_material.xml b/packages/PackageInstaller/res/layout/select_dialog_singlechoice_material.xml
new file mode 100644
index 0000000..8345b18
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/select_dialog_singlechoice_material.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?attr/textColorAlertDialogListItem"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/select_dialog_padding_start_material"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:drawableStart="?android:attr/listChoiceIndicatorSingle"
+ android:drawablePadding="@dimen/select_dialog_drawable_padding_start_material"
+ android:ellipsize="marquee" />
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index 483b0cf..18320f7 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -18,6 +18,8 @@
<resources>
<style name="Theme.AlertDialogActivity"
- parent="@android:style/Theme.DeviceDefault.Dialog.Alert" />
+ parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+ <item name="alertDialogStyle">@style/AlertDialog</item>
+ </style>
</resources>
diff --git a/packages/PackageInstaller/res/values-television/styles.xml b/packages/PackageInstaller/res/values-television/styles.xml
new file mode 100644
index 0000000..936fff0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2022 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="AlertDialog.Leanback" parent="@style/AlertDialog">
+ <item name="buttonPanelSideLayout">@layout/alert_dialog_leanback_button_panel_side</item>
+ <item name="layout">@layout/alert_dialog_leanback</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
index 5ae4957..1cc6933 100644
--- a/packages/PackageInstaller/res/values-television/themes.xml
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -17,15 +17,20 @@
<resources>
- <style name="Theme.AlertDialogActivity.NoAnimation">
+ <style name="Theme.AlertDialogActivity.NoAnimation"
+ parent="@style/Theme.AlertDialogActivity.NoActionBar">
<item name="android:windowAnimationStyle">@null</item>
</style>
<style name="Theme.AlertDialogActivity"
- parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
+ </style>
<style name="Theme.AlertDialogActivity.NoActionBar"
- parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ parent="@android:style/Theme.Material.Light.NoActionBar">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
</style>
</resources>
diff --git a/packages/PackageInstaller/res/values/attrs.xml b/packages/PackageInstaller/res/values/attrs.xml
index e220f4c..e3070e2 100644
--- a/packages/PackageInstaller/res/values/attrs.xml
+++ b/packages/PackageInstaller/res/values/attrs.xml
@@ -32,4 +32,49 @@
<attr name="circle_radius_pressed_percent" format="fraction" />
</declare-styleable>
<!-- END: Ported from WearableSupport -->
+ <declare-styleable name="Theme">
+ <attr name="alertDialogCenterButtons" format="boolean" />
+ </declare-styleable>
+ <declare-styleable name="AlertDialog">
+ <attr name="fullDark" format="reference|color" />
+ <attr name="topDark" format="reference|color" />
+ <attr name="centerDark" format="reference|color" />
+ <attr name="bottomDark" format="reference|color" />
+ <attr name="fullBright" format="reference|color" />
+ <attr name="topBright" format="reference|color" />
+ <attr name="centerBright" format="reference|color" />
+ <attr name="bottomBright" format="reference|color" />
+ <attr name="bottomMedium" format="reference|color" />
+ <attr name="centerMedium" format="reference|color" />
+ <attr name="layout" format="reference" />
+ <attr name="buttonPanelSideLayout" format="reference" />
+ <attr name="listLayout" format="reference" />
+ <attr name="multiChoiceItemLayout" format="reference" />
+ <attr name="singleChoiceItemLayout" format="reference" />
+ <attr name="listItemLayout" format="reference" />
+ <attr name="progressLayout" format="reference" />
+ <attr name="horizontalProgressLayout" format="reference" />
+ <!-- @hide Not ready for public use. -->
+ <attr name="showTitle" format="boolean" />
+ <!-- Whether fullDark, etc. should use default values if null. -->
+ <attr name="needsDefaultBackgrounds" format="boolean" />
+ <!-- Workaround until we replace AlertController with custom layout. -->
+ <attr name="controllerType">
+ <!-- The default controller. -->
+ <enum name="normal" value="0" />
+ <!-- Controller for micro specific layout. -->
+ <enum name="micro" value="1" />
+ </attr>
+ <!-- Offset when scrolling to a selection. -->
+ <attr name="selectionScrollOffset" format="dimension" />
+ </declare-styleable>
+ <declare-styleable name="ButtonBarLayout">
+ <!-- Whether to automatically stack the buttons when there is not
+ enough space to lay them out side-by-side. -->
+ <attr name="allowStacking" format="boolean" />
+ </declare-styleable>
+ <declare-styleable name="TextAppearance">
+ <!-- Size of the text. Recommended dimension type for text is "sp" for scaled-pixels (example: 15sp). -->
+ <attr name="textSize" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/PackageInstaller/res/values/dimens.xml b/packages/PackageInstaller/res/values/dimens.xml
index 112723f..bfea05e 100644
--- a/packages/PackageInstaller/res/values/dimens.xml
+++ b/packages/PackageInstaller/res/values/dimens.xml
@@ -41,4 +41,16 @@
<dimen name="wear_permission_review_pref_padding">8dp</dimen>
<dimen name="wear_permission_review_icon_size">24dp</dimen>
+
+ <!-- Dialog title height -->
+ <dimen name="alert_dialog_title_height">64dip</dimen>
+ <!-- Dialog button bar height -->
+ <dimen name="alert_dialog_button_bar_height">48dip</dimen>
+ <!-- The amount to offset when scrolling to a selection in an AlertDialog -->
+ <dimen name="config_alertDialogSelectionScrollOffset">0dp</dimen>
+ <dimen name="dialog_padding_top_material">18dp</dimen>
+ <dimen name="dialog_title_divider_material">8dp</dimen>
+ <!-- Dialog padding minus control padding, used to fix alignment. -->
+ <dimen name="select_dialog_padding_start_material">20dp</dimen>
+ <dimen name="select_dialog_drawable_padding_start_material">20dp</dimen>
</resources>
diff --git a/packages/PackageInstaller/res/values/integers.xml b/packages/PackageInstaller/res/values/integers.xml
new file mode 100644
index 0000000..22ad3a3
--- /dev/null
+++ b/packages/PackageInstaller/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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>
+ <!-- The alert controller to use for alert dialogs. -->
+ <integer name="config_alertDialogController">0</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values/styles.xml b/packages/PackageInstaller/res/values/styles.xml
new file mode 100644
index 0000000..ca797e1
--- /dev/null
+++ b/packages/PackageInstaller/res/values/styles.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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="AlertDialog">
+ <item name="fullDark">@empty</item>
+ <item name="topDark">@empty</item>
+ <item name="centerDark">@empty</item>
+ <item name="bottomDark">@empty</item>
+ <item name="fullBright">@empty</item>
+ <item name="topBright">@empty</item>
+ <item name="centerBright">@empty</item>
+ <item name="bottomBright">@empty</item>
+ <item name="bottomMedium">@empty</item>
+ <item name="centerMedium">@empty</item>
+ <item name="layout">@layout/alert_dialog_material</item>
+ <item name="listLayout">@layout/select_dialog_material</item>
+ <item name="progressLayout">@layout/progress_dialog_material</item>
+ <item name="horizontalProgressLayout">@layout/alert_dialog_progress_material</item>
+ <item name="listItemLayout">@layout/select_dialog_item_material</item>
+ <item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_material</item>
+ <item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_material</item>
+ <item name="controllerType">@integer/config_alertDialogController</item>
+ <item name="selectionScrollOffset">@dimen/config_alertDialogSelectionScrollOffset</item>
+ <item name="needsDefaultBackgrounds">false</item>
+ </style>
+ <style name="TextAppearance">
+ <item name="textSize">16sp</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
index eecf9a1..9a06229 100644
--- a/packages/PackageInstaller/res/values/themes.xml
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -23,7 +23,9 @@
</style>
<style name="Theme.AlertDialogActivity"
- parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
+ <item name="alertDialogStyle">@style/AlertDialog</item>
+ </style>
<style name="Theme.AlertDialogActivity.NoActionBar">
<item name="android:windowActionBar">false</item>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/AlertActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/AlertActivity.java
new file mode 100644
index 0000000..b96435b
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/AlertActivity.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * An activity that follows the visual style of an AlertDialog.
+ *
+ * @see #mAlert
+ * @see #mAlertParams
+ * @see #setupAlert()
+ */
+public abstract class AlertActivity extends Activity implements DialogInterface {
+
+ @UnsupportedAppUsage
+ public AlertActivity() {
+ }
+
+ /**
+ * The model for the alert.
+ *
+ * @see #mAlertParams
+ */
+ @UnsupportedAppUsage
+ protected AlertController mAlert;
+
+ /**
+ * The parameters for the alert.
+ */
+ @UnsupportedAppUsage
+ protected AlertController.AlertParams mAlertParams;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAlert = AlertController.create(this, this, getWindow());
+ mAlertParams = new AlertController.AlertParams(this);
+ }
+
+ public void cancel() {
+ finish();
+ }
+
+ public void dismiss() {
+ // This is called after the click, since we finish when handling the
+ // click, don't do that again here.
+ if (!isFinishing()) {
+ finish();
+ }
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return dispatchPopulateAccessibilityEvent(this, event);
+ }
+
+ public static boolean dispatchPopulateAccessibilityEvent(Activity act,
+ AccessibilityEvent event) {
+ event.setClassName(Dialog.class.getName());
+ event.setPackageName(act.getPackageName());
+
+ ViewGroup.LayoutParams params = act.getWindow().getAttributes();
+ boolean isFullScreen = (params.width == ViewGroup.LayoutParams.MATCH_PARENT) &&
+ (params.height == ViewGroup.LayoutParams.MATCH_PARENT);
+ event.setFullScreen(isFullScreen);
+
+ return false;
+ }
+
+ /**
+ * Sets up the alert, including applying the parameters to the alert model,
+ * and installing the alert's content.
+ *
+ * @see #mAlert
+ * @see #mAlertParams
+ */
+ @UnsupportedAppUsage
+ protected void setupAlert() {
+ mAlert.installContent(mAlertParams);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mAlert.onKeyDown(keyCode, event)) return true;
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (mAlert.onKeyUp(keyCode, event)) return true;
+ return super.onKeyUp(keyCode, event);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/AlertController.java b/packages/PackageInstaller/src/com/android/packageinstaller/AlertController.java
new file mode 100644
index 0000000..35d2518
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/AlertController.java
@@ -0,0 +1,1302 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.text.method.MovementMethod;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
+import android.view.ViewStub;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.CursorAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.ScrollView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.packageinstaller.R;
+
+import java.lang.ref.WeakReference;
+
+public class AlertController {
+ public static final int MICRO = 1;
+
+ private final Context mContext;
+ private final DialogInterface mDialogInterface;
+ protected final Window mWindow;
+
+ @UnsupportedAppUsage
+ private CharSequence mTitle;
+ protected CharSequence mMessage;
+ protected ListView mListView;
+ @UnsupportedAppUsage
+ private View mView;
+
+ private int mViewLayoutResId;
+
+ private int mViewSpacingLeft;
+ private int mViewSpacingTop;
+ private int mViewSpacingRight;
+ private int mViewSpacingBottom;
+ private boolean mViewSpacingSpecified = false;
+
+ private Button mButtonPositive;
+ private CharSequence mButtonPositiveText;
+ private Message mButtonPositiveMessage;
+
+ private Button mButtonNegative;
+ private CharSequence mButtonNegativeText;
+ private Message mButtonNegativeMessage;
+
+ private Button mButtonNeutral;
+ private CharSequence mButtonNeutralText;
+ private Message mButtonNeutralMessage;
+
+ protected ScrollView mScrollView;
+
+ private int mIconId = 0;
+ private Drawable mIcon;
+
+ private ImageView mIconView;
+ private TextView mTitleView;
+ protected TextView mMessageView;
+ private MovementMethod mMessageMovementMethod;
+ @Layout.HyphenationFrequency // will be removed in cleanup
+ private Integer mMessageHyphenationFrequency; // cleanup
+ @UnsupportedAppUsage
+ private View mCustomTitleView;
+
+ @UnsupportedAppUsage
+ private boolean mForceInverseBackground;
+
+ private ListAdapter mAdapter;
+
+ private int mCheckedItem = -1;
+
+ private int mAlertDialogLayout;
+ private int mButtonPanelSideLayout;
+ private int mListLayout;
+ private int mMultiChoiceItemLayout;
+ private int mSingleChoiceItemLayout;
+ private int mListItemLayout;
+
+ private boolean mShowTitle;
+
+ private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
+
+ private Handler mHandler;
+
+ private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Message m;
+ if (v == mButtonPositive && mButtonPositiveMessage != null) {
+ m = Message.obtain(mButtonPositiveMessage);
+ } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
+ m = Message.obtain(mButtonNegativeMessage);
+ } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
+ m = Message.obtain(mButtonNeutralMessage);
+ } else {
+ m = null;
+ }
+
+ if (m != null) {
+ m.sendToTarget();
+ }
+
+ // Post a message so we dismiss after the above handlers are executed
+ mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
+ .sendToTarget();
+ }
+ };
+
+ private static final class ButtonHandler extends Handler {
+ // Button clicks have Message.what as the BUTTON{1,2,3} constant
+ private static final int MSG_DISMISS_DIALOG = 1;
+
+ private WeakReference<DialogInterface> mDialog;
+
+ public ButtonHandler(DialogInterface dialog) {
+ mDialog = new WeakReference<>(dialog);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+
+ case DialogInterface.BUTTON_POSITIVE:
+ case DialogInterface.BUTTON_NEGATIVE:
+ case DialogInterface.BUTTON_NEUTRAL:
+ ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
+ break;
+
+ case MSG_DISMISS_DIALOG:
+ ((DialogInterface) msg.obj).dismiss();
+ }
+ }
+ }
+
+ private static boolean shouldCenterSingleButton(Context context) {
+ final TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
+ return outValue.data != 0;
+ }
+
+ public static final AlertController create(Context context, DialogInterface di, Window window) {
+ final TypedArray a = context.obtainStyledAttributes(
+ null, R.styleable.AlertDialog, R.attr.alertDialogStyle,
+ android.R.style.Theme_DeviceDefault_Settings);
+ int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0);
+ a.recycle();
+
+ switch (controllerType) {
+ case MICRO:
+ return new MicroAlertController(context, di, window);
+ default:
+ return new AlertController(context, di, window);
+ }
+ }
+
+ @UnsupportedAppUsage
+ protected AlertController(Context context, DialogInterface di, Window window) {
+ mContext = context;
+ mDialogInterface = di;
+ mWindow = window;
+ mHandler = new ButtonHandler(di);
+
+ final TypedArray a = context.obtainStyledAttributes(null,
+ R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+
+ mAlertDialogLayout = a.getResourceId(
+ R.styleable.AlertDialog_layout, R.layout.alert_dialog);
+ mButtonPanelSideLayout = a.getResourceId(
+ R.styleable.AlertDialog_buttonPanelSideLayout, 0);
+ mListLayout = a.getResourceId(
+ R.styleable.AlertDialog_listLayout, R.layout.select_dialog);
+
+ mMultiChoiceItemLayout = a.getResourceId(
+ R.styleable.AlertDialog_multiChoiceItemLayout,
+ R.layout.select_dialog_multichoice);
+ mSingleChoiceItemLayout = a.getResourceId(
+ R.styleable.AlertDialog_singleChoiceItemLayout,
+ R.layout.select_dialog_singlechoice);
+ mListItemLayout = a.getResourceId(
+ R.styleable.AlertDialog_listItemLayout,
+ R.layout.select_dialog_item);
+ mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
+
+ a.recycle();
+
+ /* We use a custom title so never request a window title */
+ window.requestFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ static boolean canTextInput(View v) {
+ if (v.onCheckIsTextEditor()) {
+ return true;
+ }
+
+ if (!(v instanceof ViewGroup)) {
+ return false;
+ }
+
+ ViewGroup vg = (ViewGroup)v;
+ int i = vg.getChildCount();
+ while (i > 0) {
+ i--;
+ v = vg.getChildAt(i);
+ if (canTextInput(v)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void installContent(AlertParams params) {
+ params.apply(this);
+ installContent();
+ }
+
+ @UnsupportedAppUsage
+ public void installContent() {
+ int contentView = selectContentView();
+ mWindow.setContentView(contentView);
+ setupView();
+ }
+
+ private int selectContentView() {
+ if (mButtonPanelSideLayout == 0) {
+ return mAlertDialogLayout;
+ }
+ if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
+ return mButtonPanelSideLayout;
+ }
+ // TODO: use layout hint side for long messages/lists
+ return mAlertDialogLayout;
+ }
+
+ @UnsupportedAppUsage
+ public void setTitle(CharSequence title) {
+ mTitle = title;
+ if (mTitleView != null) {
+ mTitleView.setText(title);
+ }
+ mWindow.setTitle(title);
+ }
+
+ /**
+ * @see AlertDialog.Builder#setCustomTitle(View)
+ */
+ @UnsupportedAppUsage
+ public void setCustomTitle(View customTitleView) {
+ mCustomTitleView = customTitleView;
+ }
+
+ @UnsupportedAppUsage
+ public void setMessage(CharSequence message) {
+ mMessage = message;
+ if (mMessageView != null) {
+ mMessageView.setText(message);
+ }
+ }
+
+ public void setMessageMovementMethod(MovementMethod movementMethod) {
+ mMessageMovementMethod = movementMethod;
+ if (mMessageView != null) {
+ mMessageView.setMovementMethod(movementMethod);
+ }
+ }
+
+ public void setMessageHyphenationFrequency(
+ @Layout.HyphenationFrequency int hyphenationFrequency) {
+ mMessageHyphenationFrequency = hyphenationFrequency;
+ if (mMessageView != null) {
+ mMessageView.setHyphenationFrequency(hyphenationFrequency);
+ }
+ }
+
+ /**
+ * Set the view resource to display in the dialog.
+ */
+ public void setView(int layoutResId) {
+ mView = null;
+ mViewLayoutResId = layoutResId;
+ mViewSpacingSpecified = false;
+ }
+
+ /**
+ * Set the view to display in the dialog.
+ */
+ @UnsupportedAppUsage
+ public void setView(View view) {
+ mView = view;
+ mViewLayoutResId = 0;
+ mViewSpacingSpecified = false;
+ }
+
+ /**
+ * Set the view to display in the dialog along with the spacing around that view
+ */
+ public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
+ int viewSpacingBottom) {
+ mView = view;
+ mViewLayoutResId = 0;
+ mViewSpacingSpecified = true;
+ mViewSpacingLeft = viewSpacingLeft;
+ mViewSpacingTop = viewSpacingTop;
+ mViewSpacingRight = viewSpacingRight;
+ mViewSpacingBottom = viewSpacingBottom;
+ }
+
+ /**
+ * Sets a hint for the best button panel layout.
+ */
+ public void setButtonPanelLayoutHint(int layoutHint) {
+ mButtonPanelLayoutHint = layoutHint;
+ }
+
+ /**
+ * Sets a click listener or a message to be sent when the button is clicked.
+ * You only need to pass one of {@code listener} or {@code msg}.
+ *
+ * @param whichButton Which button, can be one of
+ * {@link DialogInterface#BUTTON_POSITIVE},
+ * {@link DialogInterface#BUTTON_NEGATIVE}, or
+ * {@link DialogInterface#BUTTON_NEUTRAL}
+ * @param text The text to display in positive button.
+ * @param listener The {@link DialogInterface.OnClickListener} to use.
+ * @param msg The {@link Message} to be sent when clicked.
+ */
+ @UnsupportedAppUsage
+ public void setButton(int whichButton, CharSequence text,
+ DialogInterface.OnClickListener listener, Message msg) {
+
+ if (msg == null && listener != null) {
+ msg = mHandler.obtainMessage(whichButton, listener);
+ }
+
+ switch (whichButton) {
+
+ case DialogInterface.BUTTON_POSITIVE:
+ mButtonPositiveText = text;
+ mButtonPositiveMessage = msg;
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ mButtonNegativeText = text;
+ mButtonNegativeMessage = msg;
+ break;
+
+ case DialogInterface.BUTTON_NEUTRAL:
+ mButtonNeutralText = text;
+ mButtonNeutralMessage = msg;
+ break;
+
+ default:
+ throw new IllegalArgumentException("Button does not exist");
+ }
+ }
+
+ /**
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param resId the resource identifier of the drawable to use as the icon,
+ * or 0 for no icon
+ */
+ @UnsupportedAppUsage
+ public void setIcon(int resId) {
+ mIcon = null;
+ mIconId = resId;
+
+ if (mIconView != null) {
+ if (resId != 0) {
+ mIconView.setVisibility(View.VISIBLE);
+ mIconView.setImageResource(mIconId);
+ } else {
+ mIconView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * Specifies the icon to display next to the alert title.
+ *
+ * @param icon the drawable to use as the icon or null for no icon
+ */
+ @UnsupportedAppUsage
+ public void setIcon(Drawable icon) {
+ mIcon = icon;
+ mIconId = 0;
+
+ if (mIconView != null) {
+ if (icon != null) {
+ mIconView.setVisibility(View.VISIBLE);
+ mIconView.setImageDrawable(icon);
+ } else {
+ mIconView.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ /**
+ * @param attrId the attributeId of the theme-specific drawable
+ * to resolve the resourceId for.
+ *
+ * @return resId the resourceId of the theme-specific drawable
+ */
+ public int getIconAttributeResId(int attrId) {
+ TypedValue out = new TypedValue();
+ mContext.getTheme().resolveAttribute(attrId, out, true);
+ return out.resourceId;
+ }
+
+ public void setInverseBackgroundForced(boolean forceInverseBackground) {
+ mForceInverseBackground = forceInverseBackground;
+ }
+
+ @UnsupportedAppUsage
+ public ListView getListView() {
+ return mListView;
+ }
+
+ @UnsupportedAppUsage
+ public Button getButton(int whichButton) {
+ switch (whichButton) {
+ case DialogInterface.BUTTON_POSITIVE:
+ return mButtonPositive;
+ case DialogInterface.BUTTON_NEGATIVE:
+ return mButtonNegative;
+ case DialogInterface.BUTTON_NEUTRAL:
+ return mButtonNeutral;
+ default:
+ return null;
+ }
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ @UnsupportedAppUsage
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return mScrollView != null && mScrollView.executeKeyEvent(event);
+ }
+
+ @SuppressWarnings({"UnusedDeclaration"})
+ @UnsupportedAppUsage
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return mScrollView != null && mScrollView.executeKeyEvent(event);
+ }
+
+ /**
+ * Resolves whether a custom or default panel should be used. Removes the
+ * default panel if a custom panel should be used. If the resolved panel is
+ * a view stub, inflates before returning.
+ *
+ * @param customPanel the custom panel
+ * @param defaultPanel the default panel
+ * @return the panel to use
+ */
+ @Nullable
+ private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) {
+ if (customPanel == null) {
+ // Inflate the default panel, if needed.
+ if (defaultPanel instanceof ViewStub) {
+ defaultPanel = ((ViewStub) defaultPanel).inflate();
+ }
+
+ return (ViewGroup) defaultPanel;
+ }
+
+ // Remove the default panel entirely.
+ if (defaultPanel != null) {
+ final ViewParent parent = defaultPanel.getParent();
+ if (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(defaultPanel);
+ }
+ }
+
+ // Inflate the custom panel, if needed.
+ if (customPanel instanceof ViewStub) {
+ customPanel = ((ViewStub) customPanel).inflate();
+ }
+
+ return (ViewGroup) customPanel;
+ }
+
+ private void setupView() {
+ final View parentPanel = mWindow.findViewById(R.id.parentPanel);
+ final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
+ final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
+ final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
+
+ // Install custom content before setting up the title or buttons so
+ // that we can handle panel overrides.
+ final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
+ setupCustomContent(customPanel);
+
+ final View customTopPanel = customPanel.findViewById(R.id.topPanel);
+ final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
+ final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
+
+ // Resolve the correct panels and remove the defaults, if needed.
+ final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
+ final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
+ final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
+
+ setupContent(contentPanel);
+ setupButtons(buttonPanel);
+ setupTitle(topPanel);
+
+ final boolean hasCustomPanel = customPanel != null
+ && customPanel.getVisibility() != View.GONE;
+ final boolean hasTopPanel = topPanel != null
+ && topPanel.getVisibility() != View.GONE;
+ final boolean hasButtonPanel = buttonPanel != null
+ && buttonPanel.getVisibility() != View.GONE;
+
+ if (!parentPanel.isInTouchMode()) {
+ final View content = hasCustomPanel ? customPanel : contentPanel;
+ if (!requestFocusForContent(content)) {
+ requestFocusForDefaultButton();
+ }
+ }
+
+ // Only display the text spacer if we don't have buttons.
+ if (!hasButtonPanel) {
+ if (contentPanel != null) {
+ final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
+ if (spacer != null) {
+ spacer.setVisibility(View.VISIBLE);
+ }
+ }
+ mWindow.setCloseOnTouchOutsideIfNotSet(true);
+ }
+
+ if (hasTopPanel) {
+ // Only clip scrolling content to padding if we have a title.
+ if (mScrollView != null) {
+ mScrollView.setClipToPadding(true);
+ }
+
+ // Only show the divider if we have a title.
+ View divider = null;
+ if (mMessage != null || mListView != null || hasCustomPanel) {
+ if (!hasCustomPanel) {
+ divider = topPanel.findViewById(R.id.titleDividerNoCustom);
+ }
+ if (divider == null) {
+ divider = topPanel.findViewById(R.id.titleDivider);
+ }
+
+ } else {
+ divider = topPanel.findViewById(R.id.titleDividerTop);
+ }
+
+ if (divider != null) {
+ divider.setVisibility(View.VISIBLE);
+ }
+ } else {
+ if (contentPanel != null) {
+ final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
+ if (spacer != null) {
+ spacer.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ if (mListView instanceof RecycleListView) {
+ ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
+ }
+
+ // Update scroll indicators as needed.
+ if (!hasCustomPanel) {
+ final View content = mListView != null ? mListView : mScrollView;
+ if (content != null) {
+ final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
+ | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
+ content.setScrollIndicators(indicators,
+ View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
+ }
+ }
+
+ final TypedArray a = mContext.obtainStyledAttributes(
+ null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+ setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
+ hasTopPanel, hasCustomPanel, hasButtonPanel);
+ a.recycle();
+ }
+
+ private boolean requestFocusForContent(View content) {
+ if (content != null && content.requestFocus()) {
+ return true;
+ }
+
+ if (mListView != null) {
+ mListView.setSelection(0);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void requestFocusForDefaultButton() {
+ if (mButtonPositive.getVisibility() == View.VISIBLE) {
+ mButtonPositive.requestFocus();
+ } else if (mButtonNegative.getVisibility() == View.VISIBLE) {
+ mButtonNegative.requestFocus();
+ } else if (mButtonNeutral.getVisibility() == View.VISIBLE) {
+ mButtonNeutral.requestFocus();
+ }
+ }
+
+ private void setupCustomContent(ViewGroup customPanel) {
+ final View customView;
+ if (mView != null) {
+ customView = mView;
+ } else if (mViewLayoutResId != 0) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ customView = inflater.inflate(mViewLayoutResId, customPanel, false);
+ } else {
+ customView = null;
+ }
+
+ final boolean hasCustomView = customView != null;
+ if (!hasCustomView || !canTextInput(customView)) {
+ mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ }
+
+ if (hasCustomView) {
+ final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
+ custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ if (mViewSpacingSpecified) {
+ custom.setPadding(
+ mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
+ }
+
+ if (mListView != null) {
+ ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
+ }
+ } else {
+ customPanel.setVisibility(View.GONE);
+ }
+ }
+
+ protected void setupTitle(ViewGroup topPanel) {
+ if (mCustomTitleView != null && mShowTitle) {
+ // Add the custom title view directly to the topPanel layout
+ final LayoutParams lp = new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+ topPanel.addView(mCustomTitleView, 0, lp);
+
+ // Hide the title template
+ final View titleTemplate = mWindow.findViewById(R.id.title_template);
+ titleTemplate.setVisibility(View.GONE);
+ } else {
+ mIconView = (ImageView) mWindow.findViewById(R.id.icon);
+
+ final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
+ if (hasTextTitle && mShowTitle) {
+ // Display the title if a title is supplied, else hide it.
+ mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
+ mTitleView.setText(mTitle);
+
+ // Do this last so that if the user has supplied any icons we
+ // use them instead of the default ones. If the user has
+ // specified 0 then make it disappear.
+ if (mIconId != 0) {
+ mIconView.setImageResource(mIconId);
+ } else if (mIcon != null) {
+ mIconView.setImageDrawable(mIcon);
+ } else {
+ // Apply the padding from the icon to ensure the title is
+ // aligned correctly.
+ mTitleView.setPadding(mIconView.getPaddingLeft(),
+ mIconView.getPaddingTop(),
+ mIconView.getPaddingRight(),
+ mIconView.getPaddingBottom());
+ mIconView.setVisibility(View.GONE);
+ }
+ } else {
+ // Hide the title template
+ final View titleTemplate = mWindow.findViewById(R.id.title_template);
+ titleTemplate.setVisibility(View.GONE);
+ mIconView.setVisibility(View.GONE);
+ topPanel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ protected void setupContent(ViewGroup contentPanel) {
+ mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView);
+ mScrollView.setFocusable(false);
+
+ // Special case for users that only want to display a String
+ mMessageView = (TextView) contentPanel.findViewById(R.id.message);
+ if (mMessageView == null) {
+ return;
+ }
+
+ if (mMessage != null) {
+ mMessageView.setText(mMessage);
+ if (mMessageMovementMethod != null) {
+ mMessageView.setMovementMethod(mMessageMovementMethod);
+ }
+ if (mMessageHyphenationFrequency != null) {
+ mMessageView.setHyphenationFrequency(mMessageHyphenationFrequency);
+ }
+ } else {
+ mMessageView.setVisibility(View.GONE);
+ mScrollView.removeView(mMessageView);
+
+ if (mListView != null) {
+ final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
+ final int childIndex = scrollParent.indexOfChild(mScrollView);
+ scrollParent.removeViewAt(childIndex);
+ scrollParent.addView(mListView, childIndex,
+ new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ } else {
+ contentPanel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
+ if (upIndicator != null) {
+ upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
+ }
+ if (downIndicator != null) {
+ downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ protected void setupButtons(ViewGroup buttonPanel) {
+ int BIT_BUTTON_POSITIVE = 1;
+ int BIT_BUTTON_NEGATIVE = 2;
+ int BIT_BUTTON_NEUTRAL = 4;
+ int whichButtons = 0;
+ mButtonPositive = (Button) buttonPanel.findViewById(R.id.button1);
+ mButtonPositive.setOnClickListener(mButtonHandler);
+
+ if (TextUtils.isEmpty(mButtonPositiveText)) {
+ mButtonPositive.setVisibility(View.GONE);
+ } else {
+ mButtonPositive.setText(mButtonPositiveText);
+ mButtonPositive.setVisibility(View.VISIBLE);
+ whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
+ }
+
+ mButtonNegative = (Button) buttonPanel.findViewById(R.id.button2);
+ mButtonNegative.setOnClickListener(mButtonHandler);
+
+ if (TextUtils.isEmpty(mButtonNegativeText)) {
+ mButtonNegative.setVisibility(View.GONE);
+ } else {
+ mButtonNegative.setText(mButtonNegativeText);
+ mButtonNegative.setVisibility(View.VISIBLE);
+
+ whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
+ }
+
+ mButtonNeutral = (Button) buttonPanel.findViewById(R.id.button3);
+ mButtonNeutral.setOnClickListener(mButtonHandler);
+
+ if (TextUtils.isEmpty(mButtonNeutralText)) {
+ mButtonNeutral.setVisibility(View.GONE);
+ } else {
+ mButtonNeutral.setText(mButtonNeutralText);
+ mButtonNeutral.setVisibility(View.VISIBLE);
+
+ whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
+ }
+
+ if (shouldCenterSingleButton(mContext)) {
+ /*
+ * If we only have 1 button it should be centered on the layout and
+ * expand to fill 50% of the available space.
+ */
+ if (whichButtons == BIT_BUTTON_POSITIVE) {
+ centerButton(mButtonPositive);
+ } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
+ centerButton(mButtonNegative);
+ } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
+ centerButton(mButtonNeutral);
+ }
+ }
+
+ final boolean hasButtons = whichButtons != 0;
+ if (!hasButtons) {
+ buttonPanel.setVisibility(View.GONE);
+ }
+ }
+
+ private void centerButton(Button button) {
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
+ params.gravity = Gravity.CENTER_HORIZONTAL;
+ params.weight = 0.5f;
+ button.setLayoutParams(params);
+ View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
+ if (leftSpacer != null) {
+ leftSpacer.setVisibility(View.VISIBLE);
+ }
+ View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
+ if (rightSpacer != null) {
+ rightSpacer.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
+ View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
+ int fullDark = 0;
+ int topDark = 0;
+ int centerDark = 0;
+ int bottomDark = 0;
+ int fullBright = 0;
+ int topBright = 0;
+ int centerBright = 0;
+ int bottomBright = 0;
+ int bottomMedium = 0;
+
+ // If the needsDefaultBackgrounds attribute is set, we know we're
+ // inheriting from a framework style.
+ final boolean needsDefaultBackgrounds = a.getBoolean(
+ R.styleable.AlertDialog_needsDefaultBackgrounds, true);
+ if (needsDefaultBackgrounds) {
+ fullDark = R.drawable.popup_full_dark;
+ topDark = R.drawable.popup_top_dark;
+ centerDark = R.drawable.popup_center_dark;
+ bottomDark = R.drawable.popup_bottom_dark;
+ fullBright = R.drawable.popup_full_bright;
+ topBright = R.drawable.popup_top_bright;
+ centerBright = R.drawable.popup_center_bright;
+ bottomBright = R.drawable.popup_bottom_bright;
+ bottomMedium = R.drawable.popup_bottom_medium;
+ }
+
+ topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
+ topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
+ centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
+ centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
+
+ /* We now set the background of all of the sections of the alert.
+ * First collect together each section that is being displayed along
+ * with whether it is on a light or dark background, then run through
+ * them setting their backgrounds. This is complicated because we need
+ * to correctly use the full, top, middle, and bottom graphics depending
+ * on how many views they are and where they appear.
+ */
+
+ final View[] views = new View[4];
+ final boolean[] light = new boolean[4];
+ View lastView = null;
+ boolean lastLight = false;
+
+ int pos = 0;
+ if (hasTitle) {
+ views[pos] = topPanel;
+ light[pos] = false;
+ pos++;
+ }
+
+ /* The contentPanel displays either a custom text message or
+ * a ListView. If it's text we should use the dark background
+ * for ListView we should use the light background. If neither
+ * are there the contentPanel will be hidden so set it as null.
+ */
+ views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel;
+ light[pos] = mListView != null;
+ pos++;
+
+ if (hasCustomView) {
+ views[pos] = customPanel;
+ light[pos] = mForceInverseBackground;
+ pos++;
+ }
+
+ if (hasButtons) {
+ views[pos] = buttonPanel;
+ light[pos] = true;
+ }
+
+ boolean setView = false;
+ for (pos = 0; pos < views.length; pos++) {
+ final View v = views[pos];
+ if (v == null) {
+ continue;
+ }
+
+ if (lastView != null) {
+ if (!setView) {
+ lastView.setBackgroundResource(lastLight ? topBright : topDark);
+ } else {
+ lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
+ }
+ setView = true;
+ }
+
+ lastView = v;
+ lastLight = light[pos];
+ }
+
+ if (lastView != null) {
+ if (setView) {
+ bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
+ bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
+ bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
+
+ // ListViews will use the Bright background, but buttons use the
+ // Medium background.
+ lastView.setBackgroundResource(
+ lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
+ } else {
+ fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
+ fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
+
+ lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
+ }
+ }
+
+ final ListView listView = mListView;
+ if (listView != null && mAdapter != null) {
+ listView.setAdapter(mAdapter);
+ final int checkedItem = mCheckedItem;
+ if (checkedItem > -1) {
+ listView.setItemChecked(checkedItem, true);
+ listView.setSelectionFromTop(checkedItem,
+ a.getDimensionPixelSize(R.styleable.AlertDialog_selectionScrollOffset, 0));
+ }
+ }
+ }
+
+ public static class RecycleListView extends ListView {
+ private final int mPaddingTopNoTitle;
+ private final int mPaddingBottomNoButtons;
+
+ boolean mRecycleOnMeasure = true;
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public RecycleListView(Context context) {
+ this(context, null);
+ }
+
+ @UnsupportedAppUsage
+ public RecycleListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray ta = context.obtainStyledAttributes(
+ attrs, R.styleable.RecycleListView);
+ mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
+ R.styleable.RecycleListView_paddingBottomNoButtons, -1);
+ mPaddingTopNoTitle = ta.getDimensionPixelOffset(
+ R.styleable.RecycleListView_paddingTopNoTitle, -1);
+ }
+
+ public void setHasDecor(boolean hasTitle, boolean hasButtons) {
+ if (!hasButtons || !hasTitle) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
+ final int paddingRight = getPaddingRight();
+ final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
+ setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+ }
+ }
+
+ @Override
+ protected boolean recycleOnMeasure() {
+ return mRecycleOnMeasure;
+ }
+ }
+
+ public static class AlertParams {
+ @UnsupportedAppUsage
+ public final Context mContext;
+ @UnsupportedAppUsage
+ public final LayoutInflater mInflater;
+
+ @UnsupportedAppUsage
+ public int mIconId = 0;
+ @UnsupportedAppUsage
+ public Drawable mIcon;
+ public int mIconAttrId = 0;
+ @UnsupportedAppUsage
+ public CharSequence mTitle;
+ @UnsupportedAppUsage
+ public View mCustomTitleView;
+ @UnsupportedAppUsage
+ public CharSequence mMessage;
+ @UnsupportedAppUsage
+ public CharSequence mPositiveButtonText;
+ @UnsupportedAppUsage
+ public DialogInterface.OnClickListener mPositiveButtonListener;
+ @UnsupportedAppUsage
+ public CharSequence mNegativeButtonText;
+ @UnsupportedAppUsage
+ public DialogInterface.OnClickListener mNegativeButtonListener;
+ @UnsupportedAppUsage
+ public CharSequence mNeutralButtonText;
+ @UnsupportedAppUsage
+ public DialogInterface.OnClickListener mNeutralButtonListener;
+ @UnsupportedAppUsage
+ public boolean mCancelable;
+ @UnsupportedAppUsage
+ public DialogInterface.OnCancelListener mOnCancelListener;
+ @UnsupportedAppUsage
+ public DialogInterface.OnDismissListener mOnDismissListener;
+ @UnsupportedAppUsage
+ public DialogInterface.OnKeyListener mOnKeyListener;
+ @UnsupportedAppUsage
+ public CharSequence[] mItems;
+ @UnsupportedAppUsage
+ public ListAdapter mAdapter;
+ @UnsupportedAppUsage
+ public DialogInterface.OnClickListener mOnClickListener;
+ public int mViewLayoutResId;
+ @UnsupportedAppUsage
+ public View mView;
+ public int mViewSpacingLeft;
+ public int mViewSpacingTop;
+ public int mViewSpacingRight;
+ public int mViewSpacingBottom;
+ public boolean mViewSpacingSpecified = false;
+ @UnsupportedAppUsage
+ public boolean[] mCheckedItems;
+ @UnsupportedAppUsage
+ public boolean mIsMultiChoice;
+ @UnsupportedAppUsage
+ public boolean mIsSingleChoice;
+ @UnsupportedAppUsage
+ public int mCheckedItem = -1;
+ @UnsupportedAppUsage
+ public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
+ @UnsupportedAppUsage
+ public Cursor mCursor;
+ @UnsupportedAppUsage
+ public String mLabelColumn;
+ @UnsupportedAppUsage
+ public String mIsCheckedColumn;
+ public boolean mForceInverseBackground;
+ @UnsupportedAppUsage
+ public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
+ public OnPrepareListViewListener mOnPrepareListViewListener;
+ public boolean mRecycleOnMeasure = true;
+
+ /**
+ * Interface definition for a callback to be invoked before the ListView
+ * will be bound to an adapter.
+ */
+ public interface OnPrepareListViewListener {
+
+ /**
+ * Called before the ListView is bound to an adapter.
+ * @param listView The ListView that will be shown in the dialog.
+ */
+ void onPrepareListView(ListView listView);
+ }
+
+ @UnsupportedAppUsage
+ public AlertParams(Context context) {
+ mContext = context;
+ mCancelable = true;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @UnsupportedAppUsage
+ public void apply(AlertController dialog) {
+ if (mCustomTitleView != null) {
+ dialog.setCustomTitle(mCustomTitleView);
+ } else {
+ if (mTitle != null) {
+ dialog.setTitle(mTitle);
+ }
+ if (mIcon != null) {
+ dialog.setIcon(mIcon);
+ }
+ if (mIconId != 0) {
+ dialog.setIcon(mIconId);
+ }
+ if (mIconAttrId != 0) {
+ dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
+ }
+ }
+ if (mMessage != null) {
+ dialog.setMessage(mMessage);
+ }
+ if (mPositiveButtonText != null) {
+ dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
+ mPositiveButtonListener, null);
+ }
+ if (mNegativeButtonText != null) {
+ dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
+ mNegativeButtonListener, null);
+ }
+ if (mNeutralButtonText != null) {
+ dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
+ mNeutralButtonListener, null);
+ }
+ if (mForceInverseBackground) {
+ dialog.setInverseBackgroundForced(true);
+ }
+ // For a list, the client can either supply an array of items or an
+ // adapter or a cursor
+ if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
+ createListView(dialog);
+ }
+ if (mView != null) {
+ if (mViewSpacingSpecified) {
+ dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
+ mViewSpacingBottom);
+ } else {
+ dialog.setView(mView);
+ }
+ } else if (mViewLayoutResId != 0) {
+ dialog.setView(mViewLayoutResId);
+ }
+
+ /*
+ dialog.setCancelable(mCancelable);
+ dialog.setOnCancelListener(mOnCancelListener);
+ if (mOnKeyListener != null) {
+ dialog.setOnKeyListener(mOnKeyListener);
+ }
+ */
+ }
+
+ private void createListView(final AlertController dialog) {
+ final RecycleListView listView =
+ (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
+ final ListAdapter adapter;
+
+ if (mIsMultiChoice) {
+ if (mCursor == null) {
+ adapter = new ArrayAdapter<CharSequence>(
+ mContext, dialog.mMultiChoiceItemLayout, android.R.id.text1, mItems) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+ if (mCheckedItems != null) {
+ boolean isItemChecked = mCheckedItems[position];
+ if (isItemChecked) {
+ listView.setItemChecked(position, true);
+ }
+ }
+ return view;
+ }
+ };
+ } else {
+ adapter = new CursorAdapter(mContext, mCursor, false) {
+ private final int mLabelIndex;
+ private final int mIsCheckedIndex;
+
+ {
+ final Cursor cursor = getCursor();
+ mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
+ mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ CheckedTextView text = (CheckedTextView) view.findViewById(android.R.id.text1);
+ text.setText(cursor.getString(mLabelIndex));
+ listView.setItemChecked(
+ cursor.getPosition(),
+ cursor.getInt(mIsCheckedIndex) == 1);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(dialog.mMultiChoiceItemLayout,
+ parent, false);
+ }
+
+ };
+ }
+ } else {
+ final int layout;
+ if (mIsSingleChoice) {
+ layout = dialog.mSingleChoiceItemLayout;
+ } else {
+ layout = dialog.mListItemLayout;
+ }
+
+ if (mCursor != null) {
+ adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
+ new String[] { mLabelColumn }, new int[] { android.R.id.text1 });
+ } else if (mAdapter != null) {
+ adapter = mAdapter;
+ } else {
+ adapter = new CheckedItemAdapter(mContext, layout, android.R.id.text1, mItems);
+ }
+ }
+
+ if (mOnPrepareListViewListener != null) {
+ mOnPrepareListViewListener.onPrepareListView(listView);
+ }
+
+ /* Don't directly set the adapter on the ListView as we might
+ * want to add a footer to the ListView later.
+ */
+ dialog.mAdapter = adapter;
+ dialog.mCheckedItem = mCheckedItem;
+
+ if (mOnClickListener != null) {
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ mOnClickListener.onClick(dialog.mDialogInterface, position);
+ if (!mIsSingleChoice) {
+ dialog.mDialogInterface.dismiss();
+ }
+ }
+ });
+ } else if (mOnCheckboxClickListener != null) {
+ listView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ if (mCheckedItems != null) {
+ mCheckedItems[position] = listView.isItemChecked(position);
+ }
+ mOnCheckboxClickListener.onClick(
+ dialog.mDialogInterface, position, listView.isItemChecked(position));
+ }
+ });
+ }
+
+ // Attach a given OnItemSelectedListener to the ListView
+ if (mOnItemSelectedListener != null) {
+ listView.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+
+ if (mIsSingleChoice) {
+ listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ } else if (mIsMultiChoice) {
+ listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ }
+ listView.mRecycleOnMeasure = mRecycleOnMeasure;
+ dialog.mListView = listView;
+ }
+ }
+
+ private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
+ public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
+ CharSequence[] objects) {
+ super(context, resource, textViewResourceId, objects);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/AlertDialogLayout.java b/packages/PackageInstaller/src/com/android/packageinstaller/AlertDialogLayout.java
new file mode 100644
index 0000000..40fc406
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/AlertDialogLayout.java
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller;
+
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+
+import com.android.packageinstaller.R;
+
+/**
+ * Special implementation of linear layout that's capable of laying out alert
+ * dialog components.
+ * <p>
+ * A dialog consists of up to three panels. All panels are optional, and a
+ * dialog may contain only a single panel. The panels are laid out according
+ * to the following guidelines:
+ * <ul>
+ * <li>topPanel: exactly wrap_content</li>
+ * <li>contentPanel OR customPanel: at most fill_parent, first priority for
+ * extra space</li>
+ * <li>buttonPanel: at least minHeight, at most wrap_content, second
+ * priority for extra space</li>
+ * </ul>
+ */
+public class AlertDialogLayout extends LinearLayout {
+
+ public AlertDialogLayout(@Nullable Context context) {
+ super(context);
+ }
+
+ @UnsupportedAppUsage
+ public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!tryOnMeasure(widthMeasureSpec, heightMeasureSpec)) {
+ // Failed to perform custom measurement, let superclass handle it.
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ private boolean tryOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ View topPanel = null;
+ View buttonPanel = null;
+ View middlePanel = null;
+
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ final int id = child.getId();
+ switch (id) {
+ case R.id.topPanel:
+ topPanel = child;
+ break;
+ case R.id.buttonPanel:
+ buttonPanel = child;
+ break;
+ case R.id.contentPanel:
+ case R.id.customPanel:
+ if (middlePanel != null) {
+ // Both the content and custom are visible. Abort!
+ return false;
+ }
+ middlePanel = child;
+ break;
+ default:
+ // Unknown top-level child. Abort!
+ return false;
+ }
+ }
+
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+
+ int childState = 0;
+ int usedHeight = getPaddingTop() + getPaddingBottom();
+
+ if (topPanel != null) {
+ topPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+
+ usedHeight += topPanel.getMeasuredHeight();
+ childState = combineMeasuredStates(childState, topPanel.getMeasuredState());
+ }
+
+ int buttonHeight = 0;
+ int buttonWantsHeight = 0;
+ if (buttonPanel != null) {
+ buttonPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+ buttonHeight = resolveMinimumHeight(buttonPanel);
+ buttonWantsHeight = buttonPanel.getMeasuredHeight() - buttonHeight;
+
+ usedHeight += buttonHeight;
+ childState = combineMeasuredStates(childState, buttonPanel.getMeasuredState());
+ }
+
+ int middleHeight = 0;
+ if (middlePanel != null) {
+ final int childHeightSpec;
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ childHeightSpec = MeasureSpec.UNSPECIFIED;
+ } else {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.max(0, heightSize - usedHeight), heightMode);
+ }
+
+ middlePanel.measure(widthMeasureSpec, childHeightSpec);
+ middleHeight = middlePanel.getMeasuredHeight();
+
+ usedHeight += middleHeight;
+ childState = combineMeasuredStates(childState, middlePanel.getMeasuredState());
+ }
+
+ int remainingHeight = heightSize - usedHeight;
+
+ // Time for the "real" button measure pass. If we have remaining space,
+ // make the button pane bigger up to its target height. Otherwise,
+ // just remeasure the button at whatever height it needs.
+ if (buttonPanel != null) {
+ usedHeight -= buttonHeight;
+
+ final int heightToGive = Math.min(remainingHeight, buttonWantsHeight);
+ if (heightToGive > 0) {
+ remainingHeight -= heightToGive;
+ buttonHeight += heightToGive;
+ }
+
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(
+ buttonHeight, MeasureSpec.EXACTLY);
+ buttonPanel.measure(widthMeasureSpec, childHeightSpec);
+
+ usedHeight += buttonPanel.getMeasuredHeight();
+ childState = combineMeasuredStates(childState, buttonPanel.getMeasuredState());
+ }
+
+ // If we still have remaining space, make the middle pane bigger up
+ // to the maximum height.
+ if (middlePanel != null && remainingHeight > 0) {
+ usedHeight -= middleHeight;
+
+ final int heightToGive = remainingHeight;
+ remainingHeight -= heightToGive;
+ middleHeight += heightToGive;
+
+ // Pass the same height mode as we're using for the dialog itself.
+ // If it's EXACTLY, then the middle pane MUST use the entire
+ // height.
+ final int childHeightSpec = MeasureSpec.makeMeasureSpec(
+ middleHeight, heightMode);
+ middlePanel.measure(widthMeasureSpec, childHeightSpec);
+
+ usedHeight += middlePanel.getMeasuredHeight();
+ childState = combineMeasuredStates(childState, middlePanel.getMeasuredState());
+ }
+
+ // Compute desired width as maximum child width.
+ int maxWidth = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != View.GONE) {
+ maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
+ }
+ }
+
+ maxWidth += getPaddingLeft() + getPaddingRight();
+
+ final int widthSizeAndState = resolveSizeAndState(maxWidth, widthMeasureSpec, childState);
+ final int heightSizeAndState = resolveSizeAndState(usedHeight, heightMeasureSpec, 0);
+ setMeasuredDimension(widthSizeAndState, heightSizeAndState);
+
+ // If the children weren't already measured EXACTLY, we need to run
+ // another measure pass to for MATCH_PARENT widths.
+ if (widthMode != MeasureSpec.EXACTLY) {
+ forceUniformWidth(count, heightMeasureSpec);
+ }
+
+ return true;
+ }
+
+ /**
+ * Remeasures child views to exactly match the layout's measured width.
+ *
+ * @param count the number of child views
+ * @param heightMeasureSpec the original height measure spec
+ */
+ private void forceUniformWidth(int count, int heightMeasureSpec) {
+ // Pretend that the linear layout has an exact size.
+ final int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getMeasuredWidth(), MeasureSpec.EXACTLY);
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.width == LayoutParams.MATCH_PARENT) {
+ // Temporarily force children to reuse their old measured
+ // height.
+ final int oldHeight = lp.height;
+ lp.height = child.getMeasuredHeight();
+
+ // Remeasure with new dimensions.
+ measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
+ lp.height = oldHeight;
+ }
+ }
+ }
+ }
+
+ /**
+ * Attempts to resolve the minimum height of a view.
+ * <p>
+ * If the view doesn't have a minimum height set and only contains a single
+ * child, attempts to resolve the minimum height of the child view.
+ *
+ * @param v the view whose minimum height to resolve
+ * @return the minimum height
+ */
+ private int resolveMinimumHeight(View v) {
+ final int minHeight = v.getMinimumHeight();
+ if (minHeight > 0) {
+ return minHeight;
+ }
+
+ if (v instanceof ViewGroup) {
+ final ViewGroup vg = (ViewGroup) v;
+ if (vg.getChildCount() == 1) {
+ return resolveMinimumHeight(vg.getChildAt(0));
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingRight = getPaddingRight();
+ final int paddingTop = getPaddingTop();
+
+ // Where right end of child should go
+ final int width = right - left;
+ final int childRight = width - paddingRight;
+
+ // Space available for child
+ final int childSpace = width - paddingLeft - paddingRight;
+
+ final int totalLength = getMeasuredHeight();
+ final int count = getChildCount();
+ final int gravity = getGravity();
+ final int majorGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
+ final int minorGravity = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+
+ int childTop;
+ switch (majorGravity) {
+ case Gravity.BOTTOM:
+ // totalLength contains the padding already
+ childTop = paddingTop + bottom - top - totalLength;
+ break;
+
+ // totalLength contains the padding already
+ case Gravity.CENTER_VERTICAL:
+ childTop = paddingTop + (bottom - top - totalLength) / 2;
+ break;
+
+ case Gravity.TOP:
+ default:
+ childTop = paddingTop;
+ break;
+ }
+
+ final Drawable dividerDrawable = getDividerDrawable();
+ final int dividerHeight = dividerDrawable == null ?
+ 0 : dividerDrawable.getIntrinsicHeight();
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child != null && child.getVisibility() != GONE) {
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ final LayoutParams lp =
+ (LayoutParams) child.getLayoutParams();
+
+ int layoutGravity = lp.gravity;
+ if (layoutGravity < 0) {
+ layoutGravity = minorGravity;
+ }
+ final int layoutDirection = getLayoutDirection();
+ final int absoluteGravity = Gravity.getAbsoluteGravity(
+ layoutGravity, layoutDirection);
+
+ final int childLeft;
+ switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ + lp.leftMargin - lp.rightMargin;
+ break;
+
+ case Gravity.RIGHT:
+ childLeft = childRight - childWidth - lp.rightMargin;
+ break;
+
+ case Gravity.LEFT:
+ default:
+ childLeft = paddingLeft + lp.leftMargin;
+ break;
+ }
+
+ if (hasDividerBeforeChildAt(i)) {
+ childTop += dividerHeight;
+ }
+
+ childTop += lp.topMargin;
+ setChildFrame(child, childLeft, childTop, childWidth, childHeight);
+ childTop += childHeight + lp.bottomMargin;
+ }
+ }
+ }
+
+ private void setChildFrame(View child, int left, int top, int width, int height) {
+ child.layout(left, top, left + width, top + height);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/ButtonBarLayout.java b/packages/PackageInstaller/src/com/android/packageinstaller/ButtonBarLayout.java
new file mode 100644
index 0000000..826c076
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/ButtonBarLayout.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 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 android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.packageinstaller.R;
+
+/**
+ * An extension of LinearLayout that automatically switches to vertical
+ * orientation when it can't fit its child views horizontally.
+ */
+public class ButtonBarLayout extends LinearLayout {
+ /** Amount of the second button to "peek" above the fold when stacked. */
+ private static final int PEEK_BUTTON_DP = 16;
+
+ /** Whether the current configuration allows stacking. */
+ private boolean mAllowStacking;
+
+ private int mLastWidthSize = -1;
+
+ private int mMinimumHeight = 0;
+
+ @UnsupportedAppUsage
+ public ButtonBarLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
+ mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, true);
+ ta.recycle();
+ }
+
+ public void setAllowStacking(boolean allowStacking) {
+ if (mAllowStacking != allowStacking) {
+ mAllowStacking = allowStacking;
+ if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
+ setStacked(false);
+ }
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+ if (mAllowStacking) {
+ if (widthSize > mLastWidthSize && isStacked()) {
+ // We're being measured wider this time, try un-stacking.
+ setStacked(false);
+ }
+
+ mLastWidthSize = widthSize;
+ }
+
+ boolean needsRemeasure = false;
+
+ // If we're not stacked, make sure the measure spec is AT_MOST rather
+ // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
+ // know to stack the buttons.
+ final int initialWidthMeasureSpec;
+ if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+ initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
+
+ // We'll need to remeasure again to fill excess space.
+ needsRemeasure = true;
+ } else {
+ initialWidthMeasureSpec = widthMeasureSpec;
+ }
+
+ super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
+
+ if (mAllowStacking && !isStacked()) {
+ final int measuredWidth = getMeasuredWidthAndState();
+ final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
+ if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
+ setStacked(true);
+
+ // Measure again in the new orientation.
+ needsRemeasure = true;
+ }
+ }
+
+ if (needsRemeasure) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ // Compute minimum height such that, when stacked, some portion of the
+ // second button is visible.
+ int minHeight = 0;
+ final int firstVisible = getNextVisibleChildIndex(0);
+ if (firstVisible >= 0) {
+ final View firstButton = getChildAt(firstVisible);
+ final LayoutParams firstParams = (LayoutParams) firstButton.getLayoutParams();
+ minHeight += getPaddingTop() + firstButton.getMeasuredHeight()
+ + firstParams.topMargin + firstParams.bottomMargin;
+ if (isStacked()) {
+ final int secondVisible = getNextVisibleChildIndex(firstVisible + 1);
+ if (secondVisible >= 0) {
+ minHeight += getChildAt(secondVisible).getPaddingTop()
+ + PEEK_BUTTON_DP * getResources().getDisplayMetrics().density;
+ }
+ } else {
+ minHeight += getPaddingBottom();
+ }
+ }
+
+ if (getMinimumHeight() != minHeight) {
+ setMinimumHeight(minHeight);
+ }
+ }
+
+ private int getNextVisibleChildIndex(int index) {
+ for (int i = index, count = getChildCount(); i < count; i++) {
+ if (getChildAt(i).getVisibility() == View.VISIBLE) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return Math.max(mMinimumHeight, super.getMinimumHeight());
+ }
+
+ private void setStacked(boolean stacked) {
+ setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+ setGravity(stacked ? Gravity.END : Gravity.BOTTOM);
+
+ final View spacer = findViewById(R.id.spacer);
+ if (spacer != null) {
+ spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
+ }
+
+ // Reverse the child order. This is specific to the Material button
+ // bar's layout XML and will probably not generalize.
+ final int childCount = getChildCount();
+ for (int i = childCount - 2; i >= 0; i--) {
+ bringChildToFront(getChildAt(i));
+ }
+ }
+
+ private boolean isStacked() {
+ return getOrientation() == LinearLayout.VERTICAL;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DialogTitle.java b/packages/PackageInstaller/src/com/android/packageinstaller/DialogTitle.java
new file mode 100644
index 0000000..921a904
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DialogTitle.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+/**
+ * Used by dialogs to change the font size and number of lines to try to fit
+ * the text to the available space.
+ */
+public class DialogTitle extends TextView {
+
+ public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public DialogTitle(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @UnsupportedAppUsage
+ public DialogTitle(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DialogTitle(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final Layout layout = getLayout();
+ if (layout != null) {
+ final int lineCount = layout.getLineCount();
+ if (lineCount > 0) {
+ final int ellipsisCount = layout.getEllipsisCount(lineCount - 1);
+ if (ellipsisCount > 0) {
+ setSingleLine(false);
+ setMaxLines(2);
+
+ final TypedArray a = getContext().obtainStyledAttributes(null,
+ R.styleable.TextAppearance, android.R.attr.textAppearanceMedium,
+ android.R.style.TextAppearance_Medium);
+ final int textSize = a.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, 0);
+ if (textSize != 0) {
+ // textSize is already expressed in pixels
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
+ }
+ a.recycle();
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/MicroAlertController.java b/packages/PackageInstaller/src/com/android/packageinstaller/MicroAlertController.java
new file mode 100644
index 0000000..0d77f35
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/MicroAlertController.java
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+
+public class MicroAlertController extends AlertController {
+ public MicroAlertController(Context context, DialogInterface di, Window window) {
+ super(context, di, window);
+ }
+
+ @Override
+ protected void setupContent(ViewGroup contentPanel) {
+ // Special case for small screen - the scroll view is higher in hierarchy
+ mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
+
+ // Special case for users that only want to display a String
+ mMessageView = (TextView) contentPanel.findViewById(R.id.message);
+ if (mMessageView == null) {
+ return;
+ }
+
+ if (mMessage != null) {
+ mMessageView.setText(mMessage);
+ } else {
+ // no message, remove associated views
+ mMessageView.setVisibility(View.GONE);
+ contentPanel.removeView(mMessageView);
+
+ if (mListView != null) {
+ // has ListView, swap scrollView with ListView
+
+ // move topPanel into top of scrollParent
+ View topPanel = mScrollView.findViewById(R.id.topPanel);
+ ((ViewGroup) topPanel.getParent()).removeView(topPanel);
+ FrameLayout.LayoutParams topParams =
+ new FrameLayout.LayoutParams(topPanel.getLayoutParams());
+ topParams.gravity = Gravity.TOP;
+ topPanel.setLayoutParams(topParams);
+
+ // move buttonPanel into bottom of scrollParent
+ View buttonPanel = mScrollView.findViewById(R.id.buttonPanel);
+ ((ViewGroup) buttonPanel.getParent()).removeView(buttonPanel);
+ FrameLayout.LayoutParams buttonParams =
+ new FrameLayout.LayoutParams(buttonPanel.getLayoutParams());
+ buttonParams.gravity = Gravity.BOTTOM;
+ buttonPanel.setLayoutParams(buttonParams);
+
+ // remove scrollview
+ final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
+ final int childIndex = scrollParent.indexOfChild(mScrollView);
+ scrollParent.removeViewAt(childIndex);
+
+ // add list view
+ scrollParent.addView(mListView,
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // add top and button panel
+ scrollParent.addView(topPanel);
+ scrollParent.addView(buttonPanel);
+ } else {
+ // no content, just hide everything
+ contentPanel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ protected void setupTitle(ViewGroup topPanel) {
+ super.setupTitle(topPanel);
+ if (topPanel.getVisibility() == View.GONE) {
+ topPanel.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ protected void setupButtons(ViewGroup buttonPanel) {
+ super.setupButtons(buttonPanel);
+ if (buttonPanel.getVisibility() == View.GONE) {
+ buttonPanel.setVisibility(View.INVISIBLE);
+ }
+ }
+}