summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnthony Hugh <ahugh@google.com>2015-08-19 11:44:43 -0700
committerAnthony Hugh <ahugh@google.com>2015-08-21 12:44:43 -0700
commitb7da1f5f9886ff8a4ad81b0d617d442c461ca1e5 (patch)
tree5ed7a00b0c862f255b2ff0ad7585d56898055abc
parent2b9cdf4e64d49c24658a5ca7e10633364ef2c1f2 (diff)
downloadandroid_packages_apps_PackageInstaller-b7da1f5f9886ff8a4ad81b0d617d442c461ca1e5.tar.gz
android_packages_apps_PackageInstaller-b7da1f5f9886ff8a4ad81b0d617d442c461ca1e5.tar.bz2
android_packages_apps_PackageInstaller-b7da1f5f9886ff8a4ad81b0d617d442c461ca1e5.zip
Implement base version of GrantPermissions dialog
With the Emerald release, we need to support the new permission APIs. This changelist adds a dialog for the Activity.requestPermissions() API so that users can request permissions. This check in is a functional version, but not polished. will need to be a follow up CL to add the correct animations and update the UI to the appropriate redlines. The implementation for the confirmation dialog is modeled after the one in the clockwork libs/Views folder. There are some tweaks to match the designed behavior of the permission dialog. When there's more time in the future, we should try to condense this to one implementation. BUG: 23118402 Change-Id: Ic90d37a2ce8a7adacb7c4e004b0a5260b624f5c8
-rw-r--r--Android.mk8
-rw-r--r--res/drawable-watch-280dpi/ic_cc_cancel.pngbin0 -> 14774 bytes
-rw-r--r--res/drawable-watch-280dpi/ic_cc_checkmark.pngbin0 -> 14840 bytes
-rw-r--r--res/drawable-watch-hdpi/ic_cc_cancel.pngbin0 -> 678 bytes
-rw-r--r--res/drawable-watch-hdpi/ic_cc_checkmark.pngbin0 -> 760 bytes
-rw-r--r--res/drawable-watch-mdpi/ic_cc_cancel.pngbin0 -> 535 bytes
-rw-r--r--res/drawable-watch-mdpi/ic_cc_checkmark.pngbin0 -> 612 bytes
-rw-r--r--res/drawable-watch-xhdpi/ic_cc_cancel.pngbin0 -> 312 bytes
-rw-r--r--res/drawable-watch-xhdpi/ic_cc_checkmark.pngbin0 -> 421 bytes
-rw-r--r--res/drawable-watch/action_negative_bg.xml21
-rw-r--r--res/drawable-watch/action_positive_bg.xml21
-rw-r--r--res/drawable-watch/cancel_button.xml7
-rw-r--r--res/drawable-watch/confirm_button.xml7
-rw-r--r--res/layout-watch/grant_permissions.xml126
-rw-r--r--res/values-watch/colors.xml26
-rw-r--r--res/values-watch/dimens.xml28
-rw-r--r--res/values-watch/strings.xml29
-rw-r--r--res/values-watch/themes.xml46
-rw-r--r--src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java7
-rw-r--r--src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java159
-rw-r--r--src/com/android/packageinstaller/permission/ui/PermissionConfirmationViewHandler.java149
21 files changed, 630 insertions, 4 deletions
diff --git a/Android.mk b/Android.mk
index 5a53086e..9a7fcc42 100644
--- a/Android.mk
+++ b/Android.mk
@@ -9,12 +9,11 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_JAVA_LIBRARIES += \
android-support-v4 \
- android-support-v7-recyclerview \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v14-preference \
android-support-v17-preference-leanback \
- android-support-v17-leanback
+ android-support-v17-leanback \
LOCAL_RESOURCE_DIR := \
frameworks/support/v17/leanback/res \
@@ -22,11 +21,10 @@ LOCAL_RESOURCE_DIR := \
frameworks/support/v14/preference/res \
frameworks/support/v17/preference-leanback/res \
frameworks/support/v7/appcompat/res \
- frameworks/support/v7/recyclerview/res \
$(LOCAL_PATH)/res
LOCAL_AAPT_FLAGS := --auto-add-overlay \
- --extra-packages android.support.v17.leanback:android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat:android.support.v7.recyclerview
+ --extra-packages android.support.v17.leanback:android.support.v7.preference:android.support.v14.preference:android.support.v17.preference:android.support.v7.appcompat
LOCAL_PACKAGE_NAME := PackageInstaller
LOCAL_CERTIFICATE := platform
@@ -35,4 +33,6 @@ LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+include vendor/unbundled_google/libs/wearable/wearable-support.mk
+
include $(BUILD_PACKAGE)
diff --git a/res/drawable-watch-280dpi/ic_cc_cancel.png b/res/drawable-watch-280dpi/ic_cc_cancel.png
new file mode 100644
index 00000000..249b8697
--- /dev/null
+++ b/res/drawable-watch-280dpi/ic_cc_cancel.png
Binary files differ
diff --git a/res/drawable-watch-280dpi/ic_cc_checkmark.png b/res/drawable-watch-280dpi/ic_cc_checkmark.png
new file mode 100644
index 00000000..94db9ab0
--- /dev/null
+++ b/res/drawable-watch-280dpi/ic_cc_checkmark.png
Binary files differ
diff --git a/res/drawable-watch-hdpi/ic_cc_cancel.png b/res/drawable-watch-hdpi/ic_cc_cancel.png
new file mode 100644
index 00000000..a57893e1
--- /dev/null
+++ b/res/drawable-watch-hdpi/ic_cc_cancel.png
Binary files differ
diff --git a/res/drawable-watch-hdpi/ic_cc_checkmark.png b/res/drawable-watch-hdpi/ic_cc_checkmark.png
new file mode 100644
index 00000000..29f9ecd9
--- /dev/null
+++ b/res/drawable-watch-hdpi/ic_cc_checkmark.png
Binary files differ
diff --git a/res/drawable-watch-mdpi/ic_cc_cancel.png b/res/drawable-watch-mdpi/ic_cc_cancel.png
new file mode 100644
index 00000000..87fc65ab
--- /dev/null
+++ b/res/drawable-watch-mdpi/ic_cc_cancel.png
Binary files differ
diff --git a/res/drawable-watch-mdpi/ic_cc_checkmark.png b/res/drawable-watch-mdpi/ic_cc_checkmark.png
new file mode 100644
index 00000000..0989daa6
--- /dev/null
+++ b/res/drawable-watch-mdpi/ic_cc_checkmark.png
Binary files differ
diff --git a/res/drawable-watch-xhdpi/ic_cc_cancel.png b/res/drawable-watch-xhdpi/ic_cc_cancel.png
new file mode 100644
index 00000000..fec6ecb8
--- /dev/null
+++ b/res/drawable-watch-xhdpi/ic_cc_cancel.png
Binary files differ
diff --git a/res/drawable-watch-xhdpi/ic_cc_checkmark.png b/res/drawable-watch-xhdpi/ic_cc_checkmark.png
new file mode 100644
index 00000000..f98cc1ec
--- /dev/null
+++ b/res/drawable-watch-xhdpi/ic_cc_checkmark.png
Binary files differ
diff --git a/res/drawable-watch/action_negative_bg.xml b/res/drawable-watch/action_negative_bg.xml
new file mode 100644
index 00000000..f1c33b54
--- /dev/null
+++ b/res/drawable-watch/action_negative_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <shape android:shape="oval">
+ <solid android:color="@color/circular_button_disabled"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+ <item android:state_pressed="true">
+ <shape android:shape="oval">
+ <solid android:color="#757575"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#BDBDBD"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+</selector>
diff --git a/res/drawable-watch/action_positive_bg.xml b/res/drawable-watch/action_positive_bg.xml
new file mode 100644
index 00000000..bc3e88ba
--- /dev/null
+++ b/res/drawable-watch/action_positive_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <shape android:shape="oval">
+ <solid android:color="@color/circular_button_disabled"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+ <item android:state_pressed="true">
+ <shape android:shape="oval">
+ <solid android:color="#009688"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="#00BFA5"/>
+ <size android:width="40dp" android:height="40dp" />
+ </shape>
+ </item>
+</selector>
diff --git a/res/drawable-watch/cancel_button.xml b/res/drawable-watch/cancel_button.xml
new file mode 100644
index 00000000..5b16f549
--- /dev/null
+++ b/res/drawable-watch/cancel_button.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:drawable="@drawable/action_negative_bg" />
+ <item>
+ <bitmap android:src="@drawable/ic_cc_cancel" android:gravity="center" />
+ </item>
+</layer-list>
diff --git a/res/drawable-watch/confirm_button.xml b/res/drawable-watch/confirm_button.xml
new file mode 100644
index 00000000..6a895ec6
--- /dev/null
+++ b/res/drawable-watch/confirm_button.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:drawable="@drawable/action_positive_bg" />
+ <item>
+ <bitmap android:src="@drawable/ic_cc_checkmark" android:gravity="center" />
+ </item>
+</layer-list>
diff --git a/res/layout-watch/grant_permissions.xml b/res/layout-watch/grant_permissions.xml
new file mode 100644
index 00000000..80ed5b92
--- /dev/null
+++ b/res/layout-watch/grant_permissions.xml
@@ -0,0 +1,126 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/confirmation"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <ScrollView
+ android:id="@+id/scrolling_container"
+ android:overScrollMode="never"
+ android:scrollbars="none"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/current_page_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginBottom="@dimen/grant_permissions_app_breadcrumb_margin_bottom"
+ android:textAppearance="@style/GrantPermissions.BreadcrumbText" />
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/grant_permissions_app_icon_size"
+ android:layout_height="@dimen/grant_permissions_app_icon_size"
+ android:tint="@color/grant_permissions_app_color"
+ android:layout_gravity="center"
+ android:layout_marginTop="@dimen/grant_permissions_app_icon_margin_top"/>
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:textSize="20sp"
+ android:gravity="center"
+ android:fontFamily="sans-serif-condensed-light"
+ android:textAppearance="@style/GrantPermissions.TitleText"/>
+
+ <!-- TODO: Change this to use a ViewStub instead of show/hiding the two layouts -->
+ <FrameLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/button_bar_container"
+ android:layout_gravity="bottom"
+ android:background="#FF606060">
+ <android.support.wearable.view.WearableFrameLayout
+ android:id="@+id/horizontal_button_bar"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ app:layout_heightRound="96dp">
+ <Button
+ android:id="@+id/horizontal_deny_button"
+ android:layout_width="54dp"
+ android:layout_height="54dp"
+ android:layout_gravity="top|left"
+ android:layout_marginLeft="16dp"
+ android:layout_marginTop="9dp"
+ app:layout_marginLeftRound="36dp"
+ app:layout_marginTopRound="12dp"
+ android:background="@drawable/cancel_button"/>
+
+ <Button
+ android:id="@+id/horizontal_allow_button"
+ android:layout_width="54dp"
+ android:layout_height="54dp"
+ android:layout_gravity="top|right"
+ android:layout_marginRight="16dp"
+ android:layout_marginTop="9dp"
+ app:layout_marginRightRound="36dp"
+ app:layout_marginTopRound="12dp"
+ android:background="@drawable/confirm_button"/>
+ </android.support.wearable.view.WearableFrameLayout>
+
+ <android.support.wearable.view.WearableFrameLayout
+ android:id="@+id/vertical_button_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <LinearLayout
+ android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/vertical_allow_button"
+ style="@style/Widget.WearDiag.Button"/>
+
+ <Button
+ android:id="@+id/vertical_deny_button"
+ style="@style/Widget.WearDiag.Button"/>
+
+ <Button
+ android:id="@+id/vertical_deny_do_not_ask_again_button"
+ style="@style/Widget.WearDiag.Button"/>
+ </LinearLayout>
+ </android.support.wearable.view.WearableFrameLayout>
+ </FrameLayout>
+ </LinearLayout>
+ </ScrollView>
+</FrameLayout> \ No newline at end of file
diff --git a/res/values-watch/colors.xml b/res/values-watch/colors.xml
new file mode 100644
index 00000000..614e8f14
--- /dev/null
+++ b/res/values-watch/colors.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<resources>
+ <color name="grant_permissions_app_color">@color/grant_permissions_white_text_alpha_100</color>
+ <color name="grant_permissions_progress_color">@color/grant_permissions_white_text_alpha_100</color>
+ <color name="grant_permissions_title_color">@color/grant_permissions_white_text_alpha_70</color>
+
+ <color name="grant_permissions_white_text_alpha_100">@color/off_white</color>
+ <color name="grant_permissions_white_text_alpha_70">#b2eeeeee</color>
+
+ <color name="off_white">#ffeeeeee</color>
+</resources>
diff --git a/res/values-watch/dimens.xml b/res/values-watch/dimens.xml
new file mode 100644
index 00000000..29a65fdc
--- /dev/null
+++ b/res/values-watch/dimens.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<resources>
+ <dimen name="grant_permissions_app_icon_size">32dp</dimen>
+ <dimen name="grant_permissions_app_icon_margin_top">8dp</dimen>
+
+ <dimen name="grant_permissions_app_breadcrumb_margin_bottom">3dp</dimen>
+
+ <dimen name="action_dialog_z">16dp</dimen>
+
+ <!-- Confirmation Dialog -->
+ <dimen name="conf_diag_floating_height">16dp</dimen>
+
+</resources>
diff --git a/res/values-watch/strings.xml b/res/values-watch/strings.xml
new file mode 100644
index 00000000..3ec72e5e
--- /dev/null
+++ b/res/values-watch/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title for the dialog button to deny a permission grant and never ask the user again. -->
+ <string name="grant_dialog_button_deny_dont_ask_again">Deny and don\'t ask again</string>
+
+ <!-- Template for the current permission from the total number of permissions. -->
+ <string name="current_permission_template">
+ <xliff:g id="current_permission_index" example="1">%1$s</xliff:g>
+ <xliff:g id="permission_count" example="2">%2$s</xliff:g>
+ </string>
+
+ <!-- Preference row title for showing system apps. -->
+ <string name="preference_show_system_apps">Show system apps</string>
+</resources>
diff --git a/res/values-watch/themes.xml b/res/values-watch/themes.xml
new file mode 100644
index 00000000..fd49185f
--- /dev/null
+++ b/res/values-watch/themes.xml
@@ -0,0 +1,46 @@
+<?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
+ -->
+
+<resources>
+ <style name="Settings" parent="Theme.Leanback">
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Permissions</item>
+ </style>
+
+ <style name="GrantPermissions" parent="Theme.Leanback">
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowAnimationStyle">@style/Animation.Snackbar</item>
+ <item name="android:windowElevation">@dimen/action_dialog_z</item>
+ </style>
+
+ <style name="GrantPermissions.BreadcrumbText">
+ <item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/grant_permissions_progress_color</item>
+ </style>
+
+ <style name="GrantPermissions.TitleText">
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">18sp</item>
+ <item name="android:textColor">@color/grant_permissions_title_color</item>
+ <item name="android:lineSpacingMultiplier">1.221</item>
+ </style>
+
+ <style name="Animation.Snackbar" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/snackbar_enter</item>
+ <item name="android:windowExitAnimation">@anim/snackbar_exit</item>
+ </style>
+</resources>
diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
index c451dd50..c55267e0 100644
--- a/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
+++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
@@ -73,6 +73,8 @@ public class GrantPermissionsActivity extends OverlayTouchActivity
if (Utils.isTelevision(this)) {
mViewHandler = new GrantPermissionsTvViewHandler(this).setResultListener(this);
+ } else if (isWatch()) {
+ mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
} else {
mViewHandler = new GrantPermissionsDefaultViewHandler(this).setResultListener(this);
}
@@ -357,6 +359,11 @@ public class GrantPermissionsActivity extends OverlayTouchActivity
SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
}
+ private boolean isWatch() {
+ PackageManager pm = getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_WATCH);
+ }
+
private static final class GroupState {
static final int STATE_UNKNOWN = 0;
static final int STATE_ALLOWED = 1;
diff --git a/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java
new file mode 100644
index 00000000..ac573c43
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java
@@ -0,0 +1,159 @@
+package com.android.packageinstaller.permission.ui;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.packageinstaller.R;
+
+/**
+ * Watch-specific view handler for the grant permissions activity.
+ */
+final class GrantPermissionsWatchViewHandler extends PermissionConfirmationViewHandler
+ implements GrantPermissionsViewHandler {
+ private static final String TAG = "GrantPermissionsViewH";
+
+ private static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
+
+ private final Context mContext;
+
+ private ResultListener mResultListener;
+
+ private String mGroupName;
+ private boolean mShowDoNotAsk;
+
+ private CharSequence mMessage;
+ private String mCurrentPageText;
+ private Icon mIcon;
+
+ GrantPermissionsWatchViewHandler(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public GrantPermissionsWatchViewHandler setResultListener(ResultListener listener) {
+ mResultListener = listener;
+ return this;
+ }
+
+ @Override
+ public View createView() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "createView()");
+ }
+
+ mShowDoNotAsk = false;
+
+ return super.createView();
+ }
+
+ @Override
+ public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) {
+ outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ outLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ outLayoutParams.format = PixelFormat.OPAQUE;
+ outLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ outLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ }
+
+ @Override
+ public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
+ CharSequence message, boolean showDoNotAsk) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updateUi() - groupName: " + groupName
+ + ", groupCount: " + groupCount
+ + ", groupIndex: " + groupIndex
+ + ", icon: " + icon
+ + ", message: " + message
+ + ", showDoNotAsk: " + showDoNotAsk);
+ }
+
+ mGroupName = groupName;
+ mShowDoNotAsk = showDoNotAsk;
+ mMessage = message;
+ mIcon = icon;
+ mCurrentPageText = (groupCount > 1 ?
+ mContext.getString(R.string.current_permission_template, groupIndex + 1, groupCount)
+ : null);
+
+ invalidate();
+ }
+
+ @Override
+ public void saveInstanceState(Bundle outState) {
+ outState.putString(ARG_GROUP_NAME, mGroupName);
+ }
+
+ @Override
+ public void loadInstanceState(Bundle savedInstanceState) {
+ mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName, false, false);
+ }
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public void onAllow() {
+ onClick(true /* granted */, false /* doNotAskAgain */);
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public void onDeny() {
+ onClick(false /* granted */, false /* doNotAskAgain */);
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public void onDenyDoNotAskAgain() {
+ onClick(false /* granted */, true /* doNotAskAgain */);
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public CharSequence getCurrentPageText() {
+ return mCurrentPageText;
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public Icon getPermissionIcon() {
+ return mIcon;
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public CharSequence getMessage() {
+ return mMessage;
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public int getButtonBarMode() {
+ return mShowDoNotAsk ? MODE_VERTICAL_BUTTONS : MODE_HORIZONTAL_BUTTONS;
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public CharSequence getVerticalAllowText() {
+ return mContext.getString(R.string.grant_dialog_button_allow);
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public CharSequence getVerticalDenyText() {
+ return mContext.getString(R.string.grant_dialog_button_deny);
+ }
+
+ @Override // PermissionConfirmationViewHandler
+ public CharSequence getVerticalDenyDoNotAskAgainText() {
+ return mContext.getString(R.string.grant_dialog_button_deny_dont_ask_again);
+ }
+
+ private void onClick(boolean granted, boolean doNotAskAgain) {
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName, granted, doNotAskAgain);
+ }
+ }
+}
diff --git a/src/com/android/packageinstaller/permission/ui/PermissionConfirmationViewHandler.java b/src/com/android/packageinstaller/permission/ui/PermissionConfirmationViewHandler.java
new file mode 100644
index 00000000..63ed0a45
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/ui/PermissionConfirmationViewHandler.java
@@ -0,0 +1,149 @@
+package com.android.packageinstaller.permission.ui;
+
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+
+public abstract class PermissionConfirmationViewHandler implements
+ View.OnClickListener {
+ public static final int MODE_HORIZONTAL_BUTTONS = 0;
+ public static final int MODE_VERTICAL_BUTTONS = 1;
+
+ private View mRoot;
+ private TextView mCurrentPageText;
+ private ImageView mIcon;
+ private TextView mMessage;
+ private ScrollView mScrollingContainer;
+ private ViewGroup mContent;
+ private ViewGroup mHorizontalButtonBar;
+ private ViewGroup mVerticalButtonBar;
+ private Button mVerticalAllow;
+ private Button mVerticalDeny;
+ private Button mVerticalDenyDoNotAskAgain;
+ private View mButtonBarContainer;
+
+ private Context mContext;
+
+ // TODO: Move these into a builder
+ public abstract void onAllow();
+ public abstract void onDeny();
+ public abstract void onDenyDoNotAskAgain();
+ public abstract CharSequence getVerticalAllowText();
+ public abstract CharSequence getVerticalDenyText();
+ public abstract CharSequence getVerticalDenyDoNotAskAgainText();
+ public abstract CharSequence getCurrentPageText();
+ public abstract Icon getPermissionIcon();
+ public abstract CharSequence getMessage();
+
+ public PermissionConfirmationViewHandler(Context context) {
+ mContext = context;
+ }
+
+ public View createView() {
+ mRoot = LayoutInflater.from(mContext).inflate(R.layout.grant_permissions, null);
+
+ mMessage = (TextView) mRoot.findViewById(R.id.message);
+ mCurrentPageText = (TextView) mRoot.findViewById(R.id.current_page_text);
+ mIcon = (ImageView) mRoot.findViewById(R.id.icon);
+ mButtonBarContainer = mRoot.findViewById(R.id.button_bar_container);
+ mContent = (ViewGroup) mRoot.findViewById(R.id.content);
+ mScrollingContainer = (ScrollView) mRoot.findViewById(R.id.scrolling_container);
+ mHorizontalButtonBar = (ViewGroup) mRoot.findViewById(R.id.horizontal_button_bar);
+ mVerticalButtonBar = (ViewGroup) mRoot.findViewById(R.id.vertical_button_bar);
+
+ Button horizontalAllow = (Button) mRoot.findViewById(R.id.horizontal_allow_button);
+ Button horizontalDeny = (Button) mRoot.findViewById(R.id.horizontal_deny_button);
+ horizontalAllow.setOnClickListener(this);
+ horizontalDeny.setOnClickListener(this);
+
+ mVerticalAllow = (Button) mRoot.findViewById(R.id.vertical_allow_button);
+ mVerticalDeny = (Button) mRoot.findViewById(R.id.vertical_deny_button);
+ mVerticalDenyDoNotAskAgain =
+ (Button) mRoot.findViewById(R.id.vertical_deny_do_not_ask_again_button);
+ mVerticalAllow.setOnClickListener(this);
+ mVerticalDeny.setOnClickListener(this);
+ mVerticalDenyDoNotAskAgain.setOnClickListener(this);
+
+ return mRoot;
+ }
+
+ /**
+ * Child class should override this for other modes. Call invalidate() to update the UI to the
+ * new button mode.
+ * @return The current mode the layout should use for the buttons
+ */
+ public int getButtonBarMode() {
+ return MODE_HORIZONTAL_BUTTONS;
+ }
+
+ public void invalidate() {
+ CharSequence currentPageText = getCurrentPageText();
+ if (!TextUtils.isEmpty(currentPageText)) {
+ mCurrentPageText.setText(currentPageText);
+ mCurrentPageText.setVisibility(View.VISIBLE);
+ } else {
+ mCurrentPageText.setVisibility(View.INVISIBLE);
+ }
+
+ Icon icon = getPermissionIcon();
+ if (icon != null) {
+ mIcon.setImageIcon(icon);
+ mIcon.setVisibility(View.VISIBLE);
+ } else {
+ mIcon.setVisibility(View.INVISIBLE);
+ }
+
+ mMessage.setText(getMessage());
+
+ switch (getButtonBarMode()) {
+ case MODE_HORIZONTAL_BUTTONS:
+ mHorizontalButtonBar.setVisibility(View.VISIBLE);
+ mVerticalButtonBar.setVisibility(View.GONE);
+ break;
+ case MODE_VERTICAL_BUTTONS:
+ mHorizontalButtonBar.setVisibility(View.GONE);
+ mVerticalButtonBar.setVisibility(View.VISIBLE);
+ mVerticalAllow.setText(getVerticalAllowText());
+ mVerticalDeny.setText(getVerticalDenyText());
+ mVerticalDenyDoNotAskAgain.setText(getVerticalDenyDoNotAskAgainText());
+
+ mVerticalAllow.setCompoundDrawablesWithIntrinsicBounds(
+ mContext.getDrawable(R.drawable.confirm_button), null, null, null);
+ mVerticalDeny.setCompoundDrawablesWithIntrinsicBounds(
+ mContext.getDrawable(R.drawable.cancel_button), null, null, null);
+ mVerticalDenyDoNotAskAgain.setCompoundDrawablesWithIntrinsicBounds(
+ mContext.getDrawable(R.drawable.cancel_button), null, null, null);
+ break;
+ }
+
+ mScrollingContainer.scrollTo(0, 0);
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ switch (id) {
+ case R.id.horizontal_allow_button:
+ case R.id.vertical_allow_button:
+ onAllow();
+ break;
+ case R.id.horizontal_deny_button:
+ case R.id.vertical_deny_button:
+ onDeny();
+ break;
+ case R.id.vertical_deny_do_not_ask_again_button:
+ onDenyDoNotAskAgain();
+ break;
+ }
+ }
+}