diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-09-16 13:15:18 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2012-09-16 13:18:10 -0700 |
commit | 8ec226e8c778a3d1b4268edc33cd7c515d88ab31 (patch) | |
tree | d2c07b9b3ae9a1496a5780bcd81c9c2fb009e9de | |
parent | 37df032e580ab6bed29eb40b72df1f49cefd2af1 (diff) | |
download | packages_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.xml | 4 | ||||
-rw-r--r-- | res/layout/permissions_list.xml | 47 | ||||
-rw-r--r-- | res/values/strings.xml | 11 | ||||
-rw-r--r-- | src/com/android/packageinstaller/CaffeinatedScrollView.java | 75 | ||||
-rw-r--r-- | src/com/android/packageinstaller/PackageInstallerActivity.java | 148 |
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(); - } - } } |