From 37df032e580ab6bed29eb40b72df1f49cefd2af1 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 12 Sep 2012 17:33:49 -0700 Subject: New permissions UI. --- Android.mk | 2 +- res/layout/install_confirm.xml | 71 ++++--- .../packageinstaller/PackageInstallerActivity.java | 227 +++++++++++++++++---- 3 files changed, 224 insertions(+), 76 deletions(-) diff --git a/Android.mk b/Android.mk index f286016..0612cfe 100644 --- a/Android.mk +++ b/Android.mk @@ -5,7 +5,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) -#LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 LOCAL_PACKAGE_NAME := PackageInstaller LOCAL_CERTIFICATE := platform diff --git a/res/layout/install_confirm.xml b/res/layout/install_confirm.xml index 0eb8ba6..753a24b 100644 --- a/res/layout/install_confirm.xml +++ b/res/layout/install_confirm.xml @@ -25,9 +25,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="8dip" - android:paddingRight="8dip"> + android:layout_height="match_parent"> + android:paddingTop="12dip" /> - + - + + - - + android:background="@*android:drawable/tab_unselected_holo" + android:fillViewport="true" + android:scrollbars="none"> + + + + + + + + + - + + android:showDividers="beginning"> mTabs = new ArrayList(); + private final Rect mTempRect = new Rect(); + + static final class TabInfo { + private final String tag; + private final View view; + + TabInfo(String _tag, View _view) { + tag = _tag; + view = _view; + } + } + + static class DummyTabFactory implements TabHost.TabContentFactory { + private final Context mContext; + + public DummyTabFactory(Context context) { + mContext = context; + } + + @Override + public View createTabContent(String tag) { + View v = new View(mContext); + v.setMinimumWidth(0); + v.setMinimumHeight(0); + return v; + } + } + + public TabsAdapter(Activity activity, TabHost tabHost, ViewPager pager) { + mContext = activity; + mTabHost = tabHost; + mViewPager = pager; + mTabHost.setOnTabChangedListener(this); + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(TabHost.TabSpec tabSpec, View view) { + tabSpec.setContent(new DummyTabFactory(mContext)); + String tag = tabSpec.getTag(); + + TabInfo info = new TabInfo(tag, view); + mTabs.add(info); + mTabHost.addTab(tabSpec); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + View view = mTabs.get(position).view; + container.addView(view); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View)object); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public void onTabChanged(String tabId) { + int position = mTabHost.getCurrentTab(); + mViewPager.setCurrentItem(position); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + // Unfortunately when TabHost changes the current tab, it kindly + // also takes care of putting focus on it when not in touch mode. + // The jerk. + // This hack tries to prevent this from pulling focus out of our + // ViewPager. + TabWidget widget = mTabHost.getTabWidget(); + int oldFocusability = widget.getDescendantFocusability(); + widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + mTabHost.setCurrentTab(position); + widget.setDescendantFocusability(oldFocusability); + + // Scroll the current tab into visibility if needed. + View tab = widget.getChildTabViewAt(position); + mTempRect.set(tab.getLeft(), tab.getTop(), tab.getRight(), tab.getBottom()); + widget.requestRectangleOnScreen(mTempRect, false); + + // Make sure the scrollbars are visible for a moment after selection + final View contentView = mTabs.get(position).view; + if (contentView instanceof CaffeinatedScrollView) { + ((CaffeinatedScrollView) contentView).awakenScrollBars(); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + } + } private void startInstallConfirm() { - LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section); - LinearLayout securityList = (LinearLayout) permsSection.findViewById( - R.id.security_settings_list); + TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost); + tabHost.setup(); + ViewPager viewPager = (ViewPager)findViewById(R.id.pager); + TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager); + boolean permVisible = false; - if(mPkgInfo != null) { - AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo); - if(asp.getPermissionCount() > 0) { + int msg = 0; + if (mPkgInfo != null) { + AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo); + 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); + if (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0) { + scrollView.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); + } + 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); + } + if (perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE) > 0) { permVisible = true; - securityList.addView(asp.getPermissionsView()); + 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); } } - if(!permVisible){ - permsSection.setVisibility(View.INVISIBLE); + if (!permVisible) { + if (msg == 0) { + msg = R.string.install_confirm_question_no_perms; + } + tabHost.setVisibility(View.INVISIBLE); + } + if (msg != 0) { + ((TextView)findViewById(R.id.install_confirm_question)).setText(msg); } mInstallConfirm.setVisibility(View.VISIBLE); mOk = (Button)findViewById(R.id.ok_button); @@ -119,27 +285,6 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen @Override public Dialog onCreateDialog(int id, Bundle bundle) { switch (id) { - case DLG_REPLACE_APP: - int msgId = R.string.dlg_app_replacement_statement; - // Customized text for system apps - if ((mAppInfo != null) && (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - msgId = R.string.dlg_sys_app_replacement_statement; - } - return new AlertDialog.Builder(this) - .setTitle(R.string.dlg_app_replacement_title) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - startInstallConfirm(); - }}) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - Log.i(TAG, "Canceling installation"); - setResult(RESULT_CANCELED); - finish(); - }}) - .setMessage(msgId) - .setOnCancelListener(this) - .create(); case DLG_UNKNOWN_APPS: return new AlertDialog.Builder(this) .setTitle(R.string.unknown_apps_dlg_title) @@ -263,13 +408,7 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen } catch (NameNotFoundException e) { mAppInfo = null; } - if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) { - startInstallConfirm(); - } else { - if(localLOGV) Log.i(TAG, "Replacing existing package:"+ - mPkgInfo.applicationInfo.packageName); - showDialogInner(DLG_REPLACE_APP); - } + startInstallConfirm(); } void setPmResult(int pmResult) { -- cgit v1.1