From 8ec226e8c778a3d1b4268edc33cd7c515d88ab31 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 16 Sep 2012 13:15:18 -0700 Subject: 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 --- res/layout/install_confirm.xml | 4 +- res/layout/permissions_list.xml | 47 +++++++ res/values/strings.xml | 11 ++ .../packageinstaller/CaffeinatedScrollView.java | 75 +++++++++++ .../packageinstaller/PackageInstallerActivity.java | 148 ++++++++++++--------- 5 files changed, 223 insertions(+), 62 deletions(-) create mode 100644 res/layout/permissions_list.xml create mode 100644 src/com/android/packageinstaller/CaffeinatedScrollView.java diff --git a/res/layout/install_confirm.xml b/res/layout/install_confirm.xml index 753a24b6..ed7f33b5 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"> - diff --git a/res/layout/permissions_list.xml b/res/layout/permissions_list.xml new file mode 100644 index 00000000..55eb81dd --- /dev/null +++ b/res/layout/permissions_list.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 22245a4f..d5d1ab52 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -16,6 +16,7 @@ Package installer + Next Install Done @@ -38,6 +39,14 @@ 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: + + 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. + + 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. App not installed. @@ -122,6 +131,8 @@ New + + All Privacy diff --git a/src/com/android/packageinstaller/CaffeinatedScrollView.java b/src/com/android/packageinstaller/CaffeinatedScrollView.java new file mode 100644 index 00000000..723ae13c --- /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 c0820106..8cf9967a 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(); - } - } } -- cgit v1.2.3