diff --git a/src/com/android/settings/applications/AppStateInstallAppsBridge.java b/src/com/android/settings/applications/AppStateInstallAppsBridge.java index 6d22e2572cd..39a31e6b74d 100644 --- a/src/com/android/settings/applications/AppStateInstallAppsBridge.java +++ b/src/com/android/settings/applications/AppStateInstallAppsBridge.java @@ -123,9 +123,8 @@ public class AppStateInstallAppsBridge extends AppStateBaseBridge { } } - public int getSummary() { - return canInstallApps() ? R.string.external_source_trusted - : R.string.external_source_untrusted; + public boolean isPotentialAppSource() { + return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested; } @Override @@ -150,7 +149,7 @@ public class AppStateInstallAppsBridge extends AppStateBaseBridge { return false; } InstallAppsState state = (InstallAppsState) info.extraInfo; - return (state.appOpMode != AppOpsManager.MODE_DEFAULT) || state.permissionRequested; + return state.isPotentialAppSource(); } }; } diff --git a/src/com/android/settings/applications/ExternalSourcesDetails.java b/src/com/android/settings/applications/ExternalSourcesDetails.java index fd8221cb4e8..af9251c0138 100644 --- a/src/com/android/settings/applications/ExternalSourcesDetails.java +++ b/src/com/android/settings/applications/ExternalSourcesDetails.java @@ -27,6 +27,7 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener; import com.android.settings.R; import com.android.settings.applications.AppStateInstallAppsBridge.InstallAppsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; public class ExternalSourcesDetails extends AppInfoWithHeader implements OnPreferenceChangeListener { @@ -74,6 +75,18 @@ public class ExternalSourcesDetails extends AppInfoWithHeader return false; } + static CharSequence getPreferenceSummary(Context context, AppEntry entry) { + final InstallAppsState appsState; + if (entry.extraInfo instanceof InstallAppsState) { + appsState = (InstallAppsState) entry.extraInfo; + } else { + appsState = new AppStateInstallAppsBridge(context, null, null) + .createInstallAppsStateFor(entry.info.packageName, entry.info.uid); + } + return context.getString(appsState.canInstallApps() ? R.string.external_source_trusted + : R.string.external_source_untrusted); + } + private void setCanInstallApps(boolean newState) { mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, mPackageInfo.applicationInfo.uid, mPackageName, diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 0be4d9e8614..04ea4c5a5cf 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -969,7 +969,9 @@ public class InstalledAppDetails extends AppInfoBase PictureInPictureSettings.checkPackageHasPictureInPictureActivities( packageInfoWithActivities.packageName, packageInfoWithActivities.activities); - if (hasDrawOverOtherApps || hasWriteSettings || hasPictureInPictureActivities) { + boolean isPotentialAppSource = isPotentialAppSource(); + if (hasDrawOverOtherApps || hasWriteSettings || hasPictureInPictureActivities || + isPotentialAppSource) { PreferenceCategory category = new PreferenceCategory(getPrefContext()); category.setTitle(R.string.advanced_apps); screen.addPreference(category); @@ -1019,11 +1021,32 @@ public class InstalledAppDetails extends AppInfoBase }); category.addPreference(pref); } + if (isPotentialAppSource) { + Preference pref = new Preference(getPrefContext()); + pref.setTitle(R.string.install_other_apps); + pref.setKey("install_other_apps"); + pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + startAppInfoFragment(ExternalSourcesDetails.class, + getString(R.string.install_other_apps)); + return true; + } + }); + category.addPreference(pref); + } } addAppInstallerInfoPref(screen); } + private boolean isPotentialAppSource() { + AppStateInstallAppsBridge.InstallAppsState appState = + new AppStateInstallAppsBridge(getContext(), null, null) + .createInstallAppsStateFor(mPackageName, mPackageInfo.applicationInfo.uid); + return appState.isPotentialAppSource(); + } + private void addAppInstallerInfoPref(PreferenceScreen screen) { String installerPackageName = null; try { @@ -1109,6 +1132,10 @@ public class InstalledAppDetails extends AppInfoBase if (pref != null) { pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry)); } + pref = findPreference("install_other_apps"); + if (pref != null) { + pref.setSummary(ExternalSourcesDetails.getPreferenceSummary(getContext(), mAppEntry)); + } } public static void setupAppSnippet(View appSnippet, CharSequence label, Drawable icon, diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index a82ba9ee5b1..ab99c9b7c64 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -1373,8 +1373,8 @@ public class ManageApplications extends InstrumentedPreferenceFragment break; case LIST_TYPE_MANAGE_SOURCES: - holder.summary - .setText(((InstallAppsState) holder.entry.extraInfo).getSummary()); + holder.summary.setText(ExternalSourcesDetails.getPreferenceSummary(mContext, + holder.entry)); break; default: diff --git a/tests/robotests/src/com/android/settings/applications/AppStateInstallAppsBridgeTest.java b/tests/robotests/src/com/android/settings/applications/AppStateInstallAppsBridgeTest.java new file mode 100644 index 00000000000..3076080060c --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/AppStateInstallAppsBridgeTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 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.settings.applications; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.AppOpsManager; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class AppStateInstallAppsBridgeTest { + + @Test + public void testInstallAppsStateCanInstallApps() { + AppStateInstallAppsBridge.InstallAppsState appState = new AppStateInstallAppsBridge + .InstallAppsState(); + assertThat(appState.canInstallApps()).isFalse(); + + appState.permissionGranted = true; + appState.permissionRequested = true; + assertThat(appState.canInstallApps()).isTrue(); + + appState.appOpMode = AppOpsManager.MODE_ERRORED; + assertThat(appState.canInstallApps()).isFalse(); + } + + @Test + public void testInstallAppsStateIsPotentialAppSource() { + AppStateInstallAppsBridge.InstallAppsState appState = new AppStateInstallAppsBridge + .InstallAppsState(); + assertThat(appState.isPotentialAppSource()).isFalse(); + + appState.appOpMode = AppOpsManager.MODE_ERRORED; + assertThat(appState.isPotentialAppSource()).isTrue(); + + appState.permissionRequested = true; + appState.appOpMode = AppOpsManager.MODE_DEFAULT; + assertThat(appState.isPotentialAppSource()).isTrue(); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/ExternalSourcesDetailsTest.java b/tests/robotests/src/com/android/settings/applications/ExternalSourcesDetailsTest.java new file mode 100644 index 00000000000..5b2f8e07272 --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/ExternalSourcesDetailsTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 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.settings.applications; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settingslib.applications.ApplicationsState; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ExternalSourcesDetailsTest { + + @Mock + private Context mContext; + @Mock + private AppStateInstallAppsBridge.InstallAppsState mInstallAppsStateAllowed; + @Mock + private AppStateInstallAppsBridge.InstallAppsState mInstallAppsStateBlocked; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mInstallAppsStateAllowed.canInstallApps()).thenReturn(true); + when(mInstallAppsStateBlocked.canInstallApps()).thenReturn(false); + } + + @Test + public void testGetPreferenceSummary() { + ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.extraInfo = mInstallAppsStateBlocked; + ExternalSourcesDetails.getPreferenceSummary(mContext, appEntry); + verify(mContext).getString(R.string.external_source_untrusted); + appEntry.extraInfo = mInstallAppsStateAllowed; + ExternalSourcesDetails.getPreferenceSummary(mContext, appEntry); + verify(mContext).getString(R.string.external_source_trusted); + } +}