diff options
author | Philip P. Moltmann <moltmann@google.com> | 2018-08-02 12:12:55 -0700 |
---|---|---|
committer | Philip P. Moltmann <moltmann@google.com> | 2018-08-03 10:28:56 -0700 |
commit | d25ec877da0538406464101ea73139c84cde7794 (patch) | |
tree | c012e6cc6e73a6e005e86b33f808204300b9eaa2 /packages/PackageInstaller/src | |
parent | bcd72a50b7629a2115457512d20ed6bf3c99fb2e (diff) |
Make package installer a sequence of alert-dialogs.
To make sure the dialog does not change height a single content for all
steps of the sequence. We just unhide the view that should actually be
shown.
Also added a night-mode theme.
Test: Manually uninstalled, installed and update package.
atest CtsNoPermissionTestCases
CtsNoPermissionTestCases25
CtsPackageInstallTestCases
CtsPackageUninstallTestCases
CtsPackageInstallerTapjackingTestCases
Change-Id: I890bb1f2697df3af87b6cb65e460f611334523ee
Diffstat (limited to 'packages/PackageInstaller/src')
7 files changed, 131 insertions, 156 deletions
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java index 5ba2d327d7d6..54105bb8432d 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java @@ -30,41 +30,49 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.util.Log; -import android.widget.TextView; +import android.view.View; + +import com.android.internal.app.AlertActivity; import java.io.File; /** * Installation failed: Return status code to the caller or display failure UI to user */ -public class InstallFailed extends Activity { +public class InstallFailed extends AlertActivity { private static final String LOG_TAG = InstallFailed.class.getSimpleName(); /** Label of the app that failed to install */ private CharSequence mLabel; /** - * Convert an package installer status code into the user friendly label. + * Unhide the appropriate label for the statusCode. * * @param statusCode The status code from the package installer. - * - * @return The user friendly label for the status code */ - private int getExplanationFromErrorCode(int statusCode) { + private void setExplanationFromErrorCode(int statusCode) { Log.d(LOG_TAG, "Installation status code: " + statusCode); + View viewToEnable; switch (statusCode) { case PackageInstaller.STATUS_FAILURE_BLOCKED: - return R.string.install_failed_blocked; + viewToEnable = requireViewById(R.id.install_failed_blocked); + break; case PackageInstaller.STATUS_FAILURE_CONFLICT: - return R.string.install_failed_conflict; + viewToEnable = requireViewById(R.id.install_failed_conflict); + break; case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: - return R.string.install_failed_incompatible; + viewToEnable = requireViewById(R.id.install_failed_incompatible); + break; case PackageInstaller.STATUS_FAILURE_INVALID: - return R.string.install_failed_invalid_apk; + viewToEnable = requireViewById(R.id.install_failed_invalid_apk); + break; default: - return R.string.install_failed; + viewToEnable = requireViewById(R.id.install_failed); + break; } + + viewToEnable.setVisibility(View.VISIBLE); } @Override @@ -89,8 +97,6 @@ public class InstallFailed extends Activity { .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); Uri packageURI = intent.getData(); - setContentView(R.layout.install_failed); - // Set header icon and title PackageUtil.AppSnippet as; PackageManager pm = getPackageManager(); @@ -106,7 +112,12 @@ public class InstallFailed extends Activity { // Store label for dialog mLabel = as.label; - PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet); + mAlert.setIcon(as.icon); + mAlert.setTitle(as.label); + mAlert.setView(R.layout.install_content_view); + mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.done), + (ignored, ignored2) -> finish(), null); + setupAlert(); // Show out of space dialog if needed if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) { @@ -114,11 +125,7 @@ public class InstallFailed extends Activity { } // Get status messages - ((TextView) findViewById(R.id.simple_status)).setText( - getExplanationFromErrorCode(statusCode)); - - // Set up "done" button - findViewById(R.id.done_button).setOnClickListener(view -> finish()); + setExplanationFromErrorCode(statusCode); } } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index c2dd740f91e5..deb6163cbf9d 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -19,8 +19,8 @@ package com.android.packageinstaller; import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN; import android.annotation.Nullable; -import android.app.Activity; import android.app.PendingIntent; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; @@ -30,9 +30,11 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; +import android.view.View; import android.widget.Button; import android.widget.ProgressBar; +import com.android.internal.app.AlertActivity; import com.android.internal.content.PackageHelper; import java.io.File; @@ -47,7 +49,7 @@ import java.io.OutputStream; * <p>This has two phases: First send the data to the package manager, then wait until the package * manager processed the result.</p> */ -public class InstallInstalling extends Activity { +public class InstallInstalling extends AlertActivity { private static final String LOG_TAG = InstallInstalling.class.getSimpleName(); private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID"; @@ -78,11 +80,31 @@ public class InstallInstalling extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.install_installing); - ApplicationInfo appInfo = getIntent() .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); + final File sourceFile = new File(mPackageURI.getPath()); + PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile); + + mAlert.setIcon(as.icon); + mAlert.setTitle(as.label); + mAlert.setView(R.layout.install_content_view); + mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel), + (ignored, ignored2) -> { + if (mInstallingTask != null) { + mInstallingTask.cancel(true); + } + + if (mSessionId > 0) { + getPackageManager().getPackageInstaller().abandonSession(mSessionId); + mSessionId = 0; + } + + setResult(RESULT_CANCELED); + finish(); + }, null); + setupAlert(); + requireViewById(R.id.installing).setVisibility(View.VISIBLE); if ("package".equals(mPackageURI.getScheme())) { try { @@ -92,10 +114,6 @@ public class InstallInstalling extends Activity { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { - final File sourceFile = new File(mPackageURI.getPath()); - PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, - sourceFile), R.id.app_snippet); - if (savedInstanceState != null) { mSessionId = savedInstanceState.getInt(SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); @@ -153,21 +171,7 @@ public class InstallInstalling extends Activity { } } - mCancelButton = (Button) findViewById(R.id.cancel_button); - - mCancelButton.setOnClickListener(view -> { - if (mInstallingTask != null) { - mInstallingTask.cancel(true); - } - - if (mSessionId > 0) { - getPackageManager().getPackageInstaller().abandonSession(mSessionId); - mSessionId = 0; - } - - setResult(RESULT_CANCELED); - finish(); - }); + mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE); mSessionCallback = new InstallSessionCallback(); } @@ -307,7 +311,7 @@ public class InstallInstalling extends Activity { @Override public void onProgressChanged(int sessionId, float progress) { if (sessionId == mSessionId) { - ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar); + ProgressBar progressBar = requireViewById(R.id.progress); progressBar.setMax(Integer.MAX_VALUE); progressBar.setProgress((int) (Integer.MAX_VALUE * progress)); } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java index 1bc9dbd39b0a..98438cde7c68 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java @@ -29,6 +29,9 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; +import android.view.View; + +import com.android.internal.app.AlertActivity; import java.io.File; import java.io.FileOutputStream; @@ -40,7 +43,7 @@ import java.io.OutputStream; * If a package gets installed from an content URI this step loads the package and turns it into * and installation from a file. Then it re-starts the installation as usual. */ -public class InstallStaging extends Activity { +public class InstallStaging extends AlertActivity { private static final String LOG_TAG = InstallStaging.class.getSimpleName(); private static final String STAGED_FILE = "STAGED_FILE"; @@ -55,7 +58,19 @@ public class InstallStaging extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.install_staging); + mAlert.setIcon(R.drawable.ic_file_download); + mAlert.setTitle(getString(R.string.app_name_unknown)); + mAlert.setView(R.layout.install_content_view); + mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel), + (ignored, ignored2) -> { + if (mStagingTask != null) { + mStagingTask.cancel(true); + } + setResult(RESULT_CANCELED); + finish(); + }, null); + setupAlert(); + requireViewById(R.id.staging).setVisibility(View.VISIBLE); if (savedInstanceState != null) { mStagedFile = new File(savedInstanceState.getString(STAGED_FILE)); @@ -64,14 +79,6 @@ public class InstallStaging extends Activity { mStagedFile = null; } } - - findViewById(R.id.cancel_button).setOnClickListener(view -> { - if (mStagingTask != null) { - mStagingTask.cancel(true); - } - setResult(RESULT_CANCELED); - finish(); - }); } @Override diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java index 94f6b31383bd..705d3f4bdf87 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java @@ -19,6 +19,7 @@ package com.android.packageinstaller; import android.annotation.Nullable; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -26,15 +27,18 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Bundle; import android.util.Log; +import android.view.View; import android.widget.Button; +import com.android.internal.app.AlertActivity; + import java.io.File; import java.util.List; /** * Finish installation: Return status code to the caller or display "success" UI to user */ -public class InstallSuccess extends Activity { +public class InstallSuccess extends AlertActivity { private static final String LOG_TAG = InstallSuccess.class.getSimpleName(); @Override @@ -53,8 +57,6 @@ public class InstallSuccess extends Activity { intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); Uri packageURI = intent.getData(); - setContentView(R.layout.install_success); - // Set header icon and title PackageUtil.AppSnippet as; PackageManager pm = getPackageManager(); @@ -67,16 +69,20 @@ public class InstallSuccess extends Activity { as = PackageUtil.getAppSnippet(this, appInfo, sourceFile); } - PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet); - - // Set up "done" button - findViewById(R.id.done_button).setOnClickListener(view -> { - if (appInfo.packageName != null) { - Log.i(LOG_TAG, "Finished installing " + appInfo.packageName); - } - finish(); - }); - + mAlert.setIcon(as.icon); + mAlert.setTitle(as.label); + mAlert.setView(R.layout.install_content_view); + mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null, + null); + mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done), + (ignored, ignored2) -> { + if (appInfo.packageName != null) { + Log.i(LOG_TAG, "Finished installing " + appInfo.packageName); + } + finish(); + }, null); + setupAlert(); + requireViewById(R.id.install_success).setVisibility(View.VISIBLE); // Enable or disable "launch" button Intent launchIntent = getPackageManager().getLaunchIntentForPackage( appInfo.packageName); @@ -89,7 +95,7 @@ public class InstallSuccess extends Activity { } } - Button launchButton = (Button)findViewById(R.id.launch_button); + Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); if (enabled) { launchButton.setOnClickListener(view -> { try { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java deleted file mode 100644 index 1fdbd97089a3..000000000000 --- a/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2018 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 static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; - -import android.app.Activity; -import android.os.Bundle; - -class OverlayTouchActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); - super.onCreate(savedInstanceState); - } -} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 97bafe75be90..580308a4cffd 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -16,6 +16,8 @@ */ package com.android.packageinstaller; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; + import android.Manifest; import android.annotation.NonNull; import android.annotation.StringRes; @@ -45,9 +47,9 @@ import android.os.UserManager; import android.provider.Settings; import android.util.Log; import android.view.View; -import android.view.View.OnClickListener; import android.widget.Button; -import android.widget.TextView; + +import com.android.internal.app.AlertActivity; import java.io.File; @@ -61,7 +63,7 @@ import java.io.File; * Based on the user response the package is then installed by launching InstallAppConfirm * sub activity. All state transitions are handled in this activity */ -public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener { +public class PackageInstallerActivity extends AlertActivity { private static final String TAG = "PackageInstaller"; private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1; @@ -95,7 +97,6 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On // Buttons to indicate user acceptance private Button mOk; - private Button mCancel; private PackageUtil.AppSnippet mAppSnippet; @@ -119,18 +120,18 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On private boolean mEnableOk = false; private void startInstallConfirm() { - int msg; + View viewToEnable; if (mAppInfo != null) { - msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 - ? R.string.install_confirm_question_update_system - : R.string.install_confirm_question_update; + viewToEnable = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + ? requireViewById(R.id.install_confirm_question_update_system) + : requireViewById(R.id.install_confirm_question_update); } else { // This is a new application with no permissions. - msg = R.string.install_confirm_question; + viewToEnable = requireViewById(R.id.install_confirm_question); } - ((TextView) findViewById(R.id.install_confirm_question)).setText(msg); + viewToEnable.setVisibility(View.VISIBLE); mEnableOk = true; mOk.setEnabled(true); @@ -280,6 +281,8 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On @Override protected void onCreate(Bundle icicle) { + getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + super.onCreate(null); if (icicle != null) { @@ -344,7 +347,7 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On // load dummy layout with OK button disabled until we override this layout in // startInstallConfirm - bindUi(R.layout.install_confirm); + bindUi(); checkIfAllowedAndInitiateInstall(); } @@ -374,17 +377,34 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources); } - private void bindUi(int layout) { - setContentView(layout); - - mOk = (Button) findViewById(R.id.ok_button); - mCancel = (Button)findViewById(R.id.cancel_button); - mOk.setOnClickListener(this); - mCancel.setOnClickListener(this); + private void bindUi() { + mAlert.setIcon(mAppSnippet.icon); + mAlert.setTitle(mAppSnippet.label); + mAlert.setView(R.layout.install_content_view); + mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install), + (ignored, ignored2) -> { + if (mOk.isEnabled()) { + if (mSessionId != -1) { + mInstaller.setPermissionsResult(mSessionId, true); + finish(); + } else { + startInstall(); + } + } + }, null); + mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel), + (ignored, ignored2) -> { + // Cancel and finish + setResult(RESULT_CANCELED); + if (mSessionId != -1) { + mInstaller.setPermissionsResult(mSessionId, false); + } + finish(); + }, null); + setupAlert(); + mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); mOk.setEnabled(false); - - PackageUtil.initSnippetForNewApp(this, mAppSnippet, R.id.app_snippet); } /** @@ -525,26 +545,6 @@ public class PackageInstallerActivity extends OverlayTouchActivity implements On super.onBackPressed(); } - public void onClick(View v) { - if (v == mOk) { - if (mOk.isEnabled()) { - if (mSessionId != -1) { - mInstaller.setPermissionsResult(mSessionId, true); - finish(); - } else { - startInstall(); - } - } - } else if (v == mCancel) { - // Cancel and finish - setResult(RESULT_CANCELED); - if (mSessionId != -1) { - mInstaller.setPermissionsResult(mSessionId, false); - } - finish(); - } - } - private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java index ba4bf8a6b838..0e89f56d2376 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java @@ -108,26 +108,6 @@ public class PackageUtil { icon); } - /** - * Utility method to display application snippet of a new package. - * The content view should have been set on context before invoking this method. - * appSnippet view should include R.id.app_icon and R.id.app_name - * defined on it. - * - * @param pContext context of package that can load the resources - * @param as The resources to be loaded - * @param snippetId view id of app snippet view - */ - @NonNull public static View initSnippetForNewApp(@NonNull Activity pContext, - @NonNull AppSnippet as, int snippetId) { - View appSnippet = pContext.findViewById(snippetId); - if (as.icon != null) { - ((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(as.icon); - } - ((TextView)appSnippet.findViewById(R.id.app_name)).setText(as.label); - return appSnippet; - } - static public class AppSnippet { @NonNull public CharSequence label; @Nullable public Drawable icon; |