summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2012-09-16 13:15:18 -0700
committerDianne Hackborn <hackbod@google.com>2012-09-16 13:18:10 -0700
commit8ec226e8c778a3d1b4268edc33cd7c515d88ab31 (patch)
treed2c07b9b3ae9a1496a5780bcd81c9c2fb009e9de
parent37df032e580ab6bed29eb40b72df1f49cefd2af1 (diff)
downloadpackages_apps_packageinstaller-8ec226e8c778a3d1b4268edc33cd7c515d88ab31.zip
packages_apps_packageinstaller-8ec226e8c778a3d1b4268edc33cd7c515d88ab31.tar.gz
packages_apps_packageinstaller-8ec226e8c778a3d1b4268edc33cd7c515d88ab31.tar.bz2
Further adjustment of the new package installer UI.
- Privacy and Device permissions are not shown as separate tabs, but instead separate sections in the same single scrollable permissions list. - No tabs are shown when installing a new app (they are all in the new single list); two tabs are shown when installing an update: the new permissions, and all permissions. - If you are reviewing more permissions than fit on the screen, the "install" button is changed to a "next" button until you scroll through the entire list. Change-Id: I0665a797f80ba5276e782e94be97090a429e5280
-rw-r--r--res/layout/install_confirm.xml4
-rw-r--r--res/layout/permissions_list.xml47
-rw-r--r--res/values/strings.xml11
-rw-r--r--src/com/android/packageinstaller/CaffeinatedScrollView.java75
-rw-r--r--src/com/android/packageinstaller/PackageInstallerActivity.java148
5 files changed, 223 insertions, 62 deletions
diff --git a/res/layout/install_confirm.xml b/res/layout/install_confirm.xml
index 753a24b..ed7f33b 100644
--- a/res/layout/install_confirm.xml
+++ b/res/layout/install_confirm.xml
@@ -47,7 +47,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <HorizontalScrollView
+ <HorizontalScrollView android:id="@+id/tabscontainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@*android:drawable/tab_unselected_holo"
@@ -115,7 +115,7 @@
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_weight="1"
- android:text="@string/install"
+ android:text="@string/next"
android:maxLines="2"
android:filterTouchesWhenObscured="true"
style="?android:attr/buttonBarButtonStyle" />
diff --git a/res/layout/permissions_list.xml b/res/layout/permissions_list.xml
new file mode 100644
index 0000000..55eb81d
--- /dev/null
+++ b/res/layout/permissions_list.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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 is the structure for the list of all permissions.
+-->
+<com.android.packageinstaller.CaffeinatedScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrollview"
+ android:fillViewport="true">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout android:id="@+id/privacylist"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ style="?android:attr/listSeparatorTextViewStyle"
+ android:layout_marginTop="8dip"
+ android:text="@string/privacyPerms" />
+ </LinearLayout>
+ <LinearLayout android:id="@+id/devicelist"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ style="?android:attr/listSeparatorTextViewStyle"
+ android:layout_marginTop="8dip"
+ android:text="@string/devicePerms" />
+ </LinearLayout>
+ </LinearLayout>
+</com.android.packageinstaller.CaffeinatedScrollView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 22245a4..d5d1ab5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -16,6 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Package installer</string>
+ <string name="next">Next</string>
<string name="install">Install</string>
<string name="done">Done</string>
<!-- TODO REMOVE LATER -->
@@ -38,6 +39,14 @@
<string name="install_confirm_question_update_system">Do you want to install an update
to this built-in application? Your existing data will not
be lost. The updated application will get access to:</string>
+ <!-- Message for updating an existing app, no permissions [CHAR LIMIT=NONE] -->
+ <string name="install_confirm_question_update_no_perms">Do you want to install an update
+ to this existing application? Your existing data will not
+ be lost. It does not require any special access.</string>
+ <!-- Message for updating an existing system app, no permissions [CHAR LIMIT=NONE] -->
+ <string name="install_confirm_question_update_system_no_perms">Do you want to install an update
+ to this built-in application? Your existing data will not
+ be lost. It does not require any special access.</string>
<string name="install_failed">App not installed.</string>
<!-- Reason displayed when installation fails because the installation package itself is invalid
in some way (e.g., corrupt) [CHAR LIMIT=100] -->
@@ -122,6 +131,8 @@
<!-- Tab label for new permissions being added to an existing app [CHAR LIMIT=20] -->
<string name="newPerms">New</string>
+ <!-- Tab label for all permissions of an app being installed [CHAR LIMIT=20] -->
+ <string name="allPerms">All</string>
<!-- Tab label for permissions related to user privacy [CHAR LIMIT=20] -->
<string name="privacyPerms">Privacy</string>
<!-- Tab label for permissions related to device behavior [CHAR LIMIT=20] -->
diff --git a/src/com/android/packageinstaller/CaffeinatedScrollView.java b/src/com/android/packageinstaller/CaffeinatedScrollView.java
new file mode 100644
index 0000000..723ae13
--- /dev/null
+++ b/src/com/android/packageinstaller/CaffeinatedScrollView.java
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2012, 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.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+/**
+ * It's a ScrollView that knows how to stay awake.
+ */
+class CaffeinatedScrollView extends ScrollView {
+ private Runnable mFullScrollAction;
+ private int mBottomSlop;
+
+ public CaffeinatedScrollView(Context context) {
+ super(context);
+ }
+
+ public CaffeinatedScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Make this visible so we can call it
+ */
+ @Override
+ public boolean awakenScrollBars() {
+ return super.awakenScrollBars();
+ }
+
+ public void setFullScrollAction(Runnable action) {
+ mFullScrollAction = action;
+ mBottomSlop = (int)(4 * getResources().getDisplayMetrics().density);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ checkFullScrollAction();
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ checkFullScrollAction();
+ }
+
+ private void checkFullScrollAction() {
+ if (mFullScrollAction != null) {
+ int daBottom = getChildAt(0).getBottom();
+ int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
+ if ((daBottom - screenBottom) < mBottomSlop) {
+ mFullScrollAction.run();
+ mFullScrollAction = null;
+ }
+ }
+ }
+}
diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java
index c082010..8cf9967 100644
--- a/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -77,6 +77,8 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
// Buttons to indicate user acceptance
private Button mOk;
private Button mCancel;
+ CaffeinatedScrollView mScrollView = null;
+ private boolean mOkCanInstall = false;
static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
@@ -221,48 +223,71 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
boolean permVisible = false;
+ mScrollView = null;
+ mOkCanInstall = false;
int msg = 0;
if (mPkgInfo != null) {
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
+ final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
+ final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
if (mAppInfo != null) {
- permVisible = true;
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
- ScrollView scrollView = new CaffeinatedScrollView(this);
- scrollView.setFillViewport(true);
+ mScrollView = new CaffeinatedScrollView(this);
+ mScrollView.setFillViewport(true);
if (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0) {
- scrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_NEW));
+ permVisible = true;
+ mScrollView.addView(perms.getPermissionsView(
+ AppSecurityPermissions.WHICH_NEW));
} else {
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
- scrollView.addView(label);
+ mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec("new").setIndicator(
- getText(R.string.newPerms)), scrollView);
- }
- if (perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL) > 0) {
- permVisible = true;
- ScrollView scrollView = new CaffeinatedScrollView(this);
- scrollView.setFillViewport(true);
- scrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
- adapter.addTab(tabHost.newTabSpec("personal").setIndicator(
- getText(R.string.privacyPerms)), scrollView);
+ getText(R.string.newPerms)), mScrollView);
+ } else {
+ findViewById(R.id.tabscontainer).setVisibility(View.GONE);
}
- if (perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE) > 0) {
+ if (NP > 0 || ND > 0) {
permVisible = true;
- ScrollView scrollView = new CaffeinatedScrollView(this);
- scrollView.setFillViewport(true);
- scrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
- adapter.addTab(tabHost.newTabSpec("device").setIndicator(
- getText(R.string.devicePerms)), scrollView);
+ LayoutInflater inflater = (LayoutInflater)getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View root = inflater.inflate(R.layout.permissions_list, null);
+ if (mScrollView == null) {
+ mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
+ }
+ if (NP > 0) {
+ ((ViewGroup)root.findViewById(R.id.privacylist)).addView(
+ perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
+ } else {
+ root.findViewById(R.id.privacylist).setVisibility(View.GONE);
+ }
+ if (ND > 0) {
+ ((ViewGroup)root.findViewById(R.id.devicelist)).addView(
+ perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
+ } else {
+ root.findViewById(R.id.devicelist).setVisibility(View.GONE);
+ }
+ adapter.addTab(tabHost.newTabSpec("all").setIndicator(
+ getText(R.string.allPerms)), root);
}
}
if (!permVisible) {
if (msg == 0) {
- msg = R.string.install_confirm_question_no_perms;
+ if (mAppInfo != null) {
+ // This is an update to an application, but there are no
+ // permissions at all.
+ msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ ? R.string.install_confirm_question_update_system_no_perms
+ : R.string.install_confirm_question_update_no_perms;
+ } else {
+ // This is a new application with no permissions.
+ msg = R.string.install_confirm_question_no_perms;
+ }
}
tabHost.setVisibility(View.INVISIBLE);
}
@@ -274,6 +299,20 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
+ if (mScrollView == null) {
+ // There is nothing to scroll view, so the ok button is immediately
+ // set to install.
+ mOk.setText(R.string.install);
+ mOkCanInstall = true;
+ } else {
+ mScrollView.setFullScrollAction(new Runnable() {
+ @Override
+ public void run() {
+ mOk.setText(R.string.install);
+ mOkCanInstall = true;
+ }
+ });
+ }
}
private void showDialogInner(int id) {
@@ -500,50 +539,39 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen
public void onClick(View v) {
if(v == mOk) {
- // Start subactivity to actually install the application
- Intent newIntent = new Intent();
- newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
- mPkgInfo.applicationInfo);
- newIntent.setData(mPackageURI);
- newIntent.setClass(this, InstallAppProgress.class);
- String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
- if (mOriginatingURI != null) {
- newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
- }
- if (mReferrerURI != null) {
- newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
- }
- if (installerPackageName != null) {
- newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
- }
- if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
- newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
- newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ if (mOkCanInstall || mScrollView == null) {
+ // Start subactivity to actually install the application
+ Intent newIntent = new Intent();
+ newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
+ mPkgInfo.applicationInfo);
+ newIntent.setData(mPackageURI);
+ newIntent.setClass(this, InstallAppProgress.class);
+ String installerPackageName = getIntent().getStringExtra(
+ Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ if (mOriginatingURI != null) {
+ newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
+ }
+ if (mReferrerURI != null) {
+ newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
+ }
+ if (installerPackageName != null) {
+ newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ installerPackageName);
+ }
+ if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ }
+ if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
+ startActivity(newIntent);
+ finish();
+ } else {
+ mScrollView.pageScroll(View.FOCUS_DOWN);
}
- if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
- startActivity(newIntent);
- finish();
} else if(v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
finish();
}
}
-
- /**
- * It's a ScrollView that knows how to stay awake.
- */
- static class CaffeinatedScrollView extends ScrollView {
- public CaffeinatedScrollView(Context context) {
- super(context);
- }
-
- /**
- * Make this visible so we can call it
- */
- @Override
- public boolean awakenScrollBars() {
- return super.awakenScrollBars();
- }
- }
}