/* * 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.permissioncontroller.permission.ui.handheld; import static android.Manifest.permission_group.STORAGE; import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY; import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; import android.app.ActionBar; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; import android.text.BidiFormatter; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RadioButton; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.widget.NestedScrollView; import androidx.fragment.app.DialogFragment; import androidx.lifecycle.ViewModelProvider; import com.android.permissioncontroller.R; import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; import com.android.permissioncontroller.permission.utils.KotlinUtils; import com.android.permissioncontroller.permission.utils.Utils; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.widget.ActionBarShadowController; import java.util.Map; import java.util.Objects; import kotlin.Pair; /** * Show and manage a single permission group for an app. * *
Allows the user to control whether the app is granted the permission.
*/
public class AppPermissionFragment extends SettingsWithLargeHeader
implements AppPermissionViewModel.ConfirmDialogShowingFragment {
private static final String LOG_TAG = "AppPermissionFragment";
private static final long POST_DELAY_MS = 20;
static final String GRANT_CATEGORY = "grant_category";
private @NonNull AppPermissionViewModel mViewModel;
private @NonNull RadioButton mAllowButton;
private @NonNull RadioButton mAllowAlwaysButton;
private @NonNull RadioButton mAllowForegroundButton;
private @NonNull RadioButton mAskOneTimeButton;
private @NonNull RadioButton mAskButton;
private @NonNull RadioButton mDenyButton;
private @NonNull RadioButton mDenyForegroundButton;
private @NonNull View mDivider;
private @NonNull ViewGroup mWidgetFrame;
private @NonNull TextView mPermissionDetails;
private @NonNull NestedScrollView mNestedScrollView;
private @NonNull String mPackageName;
private @NonNull String mPermGroupName;
private @NonNull UserHandle mUser;
private boolean mIsStorageGroup;
private boolean mIsInitialLoad;
private long mSessionId;
private @NonNull String mPackageLabel;
private @NonNull String mPermGroupLabel;
private Drawable mPackageIcon;
private Utils.ForegroundCapableType mForegroundCapableType;
/**
* Create a bundle with the arguments needed by this fragment
*
* @param packageName The name of the package
* @param permName The name of the permission whose group this fragment is for (optional)
* @param groupName The name of the permission group (required if permName not specified)
* @param userHandle The user of the app permission group
* @param caller The name of the fragment we called from
* @param sessionId The current session ID
* @param grantCategory The grant status of this app permission group. Used to initially set
* the button state
* @return A bundle with all of the args placed
*/
public static Bundle createArgs(@NonNull String packageName,
@Nullable String permName, @Nullable String groupName,
@NonNull UserHandle userHandle, @Nullable String caller, long sessionId, @Nullable
String grantCategory) {
Bundle arguments = new Bundle();
arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
if (groupName == null) {
arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName);
} else {
arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName);
}
arguments.putParcelable(Intent.EXTRA_USER, userHandle);
arguments.putString(EXTRA_CALLER_NAME, caller);
arguments.putLong(EXTRA_SESSION_ID, sessionId);
arguments.putString(GRANT_CATEGORY, grantCategory);
return arguments;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
ActionBar ab = getActivity().getActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME);
if (mPermGroupName == null) {
mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
}
mIsStorageGroup = Objects.equals(mPermGroupName, STORAGE);
mUser = getArguments().getParcelable(Intent.EXTRA_USER);
mPackageLabel = BidiFormatter.getInstance().unicodeWrap(
KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName,
mUser));
mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(),
mPermGroupName).toString();
mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(),
mPackageName, mUser);
try {
mForegroundCapableType = Utils.getForegroundCapableType(getContext(), mPackageName);
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG, "Package " + mPackageName + " not found", e);
}
mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID);
AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory(
getActivity().getApplication(), mPackageName, mPermGroupName, mUser, mSessionId,
mForegroundCapableType);
mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class);
Handler delayHandler = new Handler(Looper.getMainLooper());
mViewModel.getButtonStateLiveData().observe(this, buttonState -> {
if (mIsInitialLoad) {
setRadioButtonsState(buttonState);
} else {
delayHandler.removeCallbacksAndMessages(null);
delayHandler.postDelayed(() -> setRadioButtonsState(buttonState), POST_DELAY_MS);
}
});
mViewModel.getDetailResIdLiveData().observe(this, this::setDetail);
mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail);
if (mIsStorageGroup) {
mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Context context = getContext();
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.app_permission, container, false);
mIsInitialLoad = true;
setHeader(mPackageIcon, mPackageLabel, null, null, false);
updateHeader(root.requireViewById(R.id.large_header));
((TextView) root.requireViewById(R.id.permission_message)).setText(
context.getString(R.string.app_permission_header, mPermGroupLabel));
String caller = getArguments().getString(EXTRA_CALLER_NAME);
TextView footer1Link = root.requireViewById(R.id.footer_link_1);
footer1Link.setText(context.getString(R.string.app_permission_footer_app_permissions_link,
mPackageLabel));
setBottomLinkState(footer1Link, caller, Intent.ACTION_MANAGE_APP_PERMISSIONS);
TextView footer2Link = root.requireViewById(R.id.footer_link_2);
footer2Link.setText(context.getString(R.string.app_permission_footer_permission_apps_link));
setBottomLinkState(footer2Link, caller, Intent.ACTION_MANAGE_PERMISSION_APPS);
mAllowButton = root.requireViewById(R.id.allow_radio_button);
mAllowAlwaysButton = root.requireViewById(R.id.allow_always_radio_button);
mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button);
mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button);
mAskButton = root.requireViewById(R.id.ask_radio_button);
mDenyButton = root.requireViewById(R.id.deny_radio_button);
mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button);
mDivider = root.requireViewById(R.id.two_target_divider);
mWidgetFrame = root.requireViewById(R.id.widget_frame);
mPermissionDetails = root.requireViewById(R.id.permission_details);
mNestedScrollView = root.requireViewById(R.id.nested_scroll_view);
if (mViewModel.getButtonStateLiveData().getValue() != null) {
setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue());
} else {
mAllowButton.setVisibility(View.GONE);
mAllowAlwaysButton.setVisibility(View.GONE);
mAllowForegroundButton.setVisibility(View.GONE);
mAskOneTimeButton.setVisibility(View.GONE);
mAskButton.setVisibility(View.GONE);
mDenyButton.setVisibility(View.GONE);
mDenyForegroundButton.setVisibility(View.GONE);
}
if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) {
setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue(), root);
} else {
TextView storageFooter = root.requireViewById(R.id.footer_storage_special_app_access);
storageFooter.setVisibility(View.GONE);
}
getActivity().setTitle(
getPreferenceManager().getContext().getString(R.string.app_permission_title,
mPermGroupLabel));
return root;
}
private void setBottomLinkState(TextView view, String caller, String action) {
if ((Objects.equals(caller, AppPermissionGroupsFragment.class.getName())
&& action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS))
|| (Objects.equals(caller, PermissionAppsFragment.class.getName())
&& action.equals(Intent.ACTION_MANAGE_PERMISSION_APPS))) {
view.setVisibility(View.GONE);
} else {
view.setOnClickListener((v) -> {
Bundle args;
if (action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) {
args = AppPermissionGroupsFragment.createArgs(mPackageName, mUser,
mSessionId, true);
} else {
args = PermissionAppsFragment.createArgs(mPermGroupName, mSessionId);
}
mViewModel.showBottomLinkPage(this, action, args);
});
}
}
private void setSpecialStorageState(FullStoragePackageState storageState) {
setSpecialStorageState(storageState, getView());
}
@Override
public void onStart() {
super.onStart();
ActionBar ab = getActivity().getActionBar();
if (ab != null) {
ab.setElevation(0);
}
ActionBarShadowController.attachToView(getActivity(), getLifecycle(), mNestedScrollView);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
pressBack(this);
return true;
}
return super.onOptionsItemSelected(item);
}
private void setRadioButtonsState(Map