blob: a77c3d1f878e23f590dd1159c19798575b940092 [file] [log] [blame]
/*
* Copyright 2019, 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.managedprovisioning.preprovisioning.consent;
import static com.android.internal.util.Preconditions.checkNotNull;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.managedprovisioning.R;
import com.android.managedprovisioning.common.AccessibilityContextMenuMaker;
import com.android.managedprovisioning.common.ClickableSpanFactory;
import com.android.managedprovisioning.common.ProvisionLogger;
import com.android.managedprovisioning.common.StringConcatenator;
import com.android.managedprovisioning.common.TouchTargetEnforcer;
import com.android.managedprovisioning.common.Utils;
import com.android.managedprovisioning.model.CustomizationParams;
import com.android.managedprovisioning.preprovisioning.PreProvisioningController.UiParams;
import com.android.managedprovisioning.preprovisioning.anim.BenefitsAnimation;
import com.google.android.setupdesign.GlifLayout;
import java.util.ArrayList;
import java.util.List;
/**
* Implements functionality for the legacy consent screen.
* <p>Android Auto still uses the old UI, so we need to ensure compatibility with it.
*/
class LegacyConsentUiHelper implements ConsentUiHelper {
private static final List<Integer> SLIDE_CAPTIONS = createImmutableList(
R.string.info_anim_title_0,
R.string.info_anim_title_1,
R.string.info_anim_title_2);
private static final List<Integer> SLIDE_CAPTIONS_COMP = createImmutableList(
R.string.info_anim_title_0,
R.string.one_place_for_work_apps,
R.string.info_anim_title_2);
private final Activity mActivity;
private final ClickableSpanFactory mClickableSpanFactory;
private final TouchTargetEnforcer mTouchTargetEnforcer;
private final ConsentUiHelperCallback mCallback;
private final Utils mUtils;
private @Nullable BenefitsAnimation mBenefitsAnimation;
private final AccessibilityContextMenuMaker mContextMenuMaker;
LegacyConsentUiHelper(Activity activity, AccessibilityContextMenuMaker contextMenuMaker,
ConsentUiHelperCallback callback, Utils utils) {
mActivity = checkNotNull(activity);
mContextMenuMaker = contextMenuMaker;
mCallback = callback;
mTouchTargetEnforcer =
new TouchTargetEnforcer(activity.getResources().getDisplayMetrics().density);
mClickableSpanFactory = new ClickableSpanFactory(mActivity.getColor(R.color.blue_text));
mUtils = utils;
}
@Override
public void onStart() {
if (mBenefitsAnimation != null) {
mBenefitsAnimation.start();
}
}
@Override
public void onStop() {
if (mBenefitsAnimation != null) {
mBenefitsAnimation.stop();
}
}
private void setDpcIconAndLabel(@NonNull String appName, Drawable packageIcon, String orgName) {
if (packageIcon == null || TextUtils.isEmpty(appName)) {
return;
}
// make a container with all parts of DPC app description visible
mActivity.findViewById(R.id.intro_device_owner_app_info_container)
.setVisibility(View.VISIBLE);
if (TextUtils.isEmpty(orgName)) {
orgName = mActivity.getString(R.string.your_organization_beginning);
}
String message = mActivity.getString(R.string.your_org_app_used, orgName);
TextView appInfoText = mActivity.findViewById(R.id.device_owner_app_info_text);
appInfoText.setText(message);
ImageView imageView = mActivity.findViewById(R.id.device_manager_icon_view);
imageView.setImageDrawable(packageIcon);
imageView.setContentDescription(
mActivity.getResources().getString(R.string.mdm_icon_label, appName));
TextView deviceManagerName = mActivity.findViewById(R.id.device_manager_name);
deviceManagerName.setText(appName);
}
// TODO: refactor complex localized string assembly to an abstraction http://b/34288292
// there is a bit of copy-paste, and some details easy to forget (e.g. setMovementMethod)
private Spannable assembleDOTermsMessage(
@NonNull String termsHeaders, String orgName, Intent showTermsIntent) {
String linkText = mActivity.getString(R.string.view_terms);
if (TextUtils.isEmpty(orgName)) {
orgName = mActivity.getString(R.string.your_organization_middle);
}
String messageText = termsHeaders.isEmpty()
? mActivity.getString(R.string.device_owner_info, orgName, linkText)
: mActivity
.getString(R.string.device_owner_info_with_terms_headers, orgName, termsHeaders,
linkText);
Spannable result = new SpannableString(messageText);
int start = messageText.indexOf(linkText);
ClickableSpan span = mClickableSpanFactory.create(showTermsIntent);
result.setSpan(span, start, start + linkText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return result;
}
private static List<Integer> createImmutableList(int... values) {
if (values == null || values.length == 0) {
return emptyList();
}
List<Integer> result = new ArrayList<>(values.length);
for (int value : values) {
result.add(value);
}
return unmodifiableList(result);
}
@Override
public void initiateUi(UiParams uiParams) {
int layoutResId = 0;
int titleResId = 0;
int headerResId = 0;
String packageLabel = null;
Drawable packageIcon = null;
if (mUtils.isProfileOwnerAction(uiParams.provisioningAction)) {
titleResId = R.string.setup_profile;
headerResId = R.string.work_profile_provisioning_accept_header;
layoutResId = R.layout.intro_profile_owner;
} else if (mUtils.isDeviceOwnerAction(uiParams.provisioningAction)) {
titleResId = R.string.setup_device;
headerResId = R.string.fully_managed_device_provisioning_accept_header;
layoutResId = R.layout.intro_device_owner;
packageLabel = uiParams.packageInfo != null
? uiParams.packageInfo.appLabel
: uiParams.deviceAdminLabel != null
? uiParams.deviceAdminLabel : uiParams.packageName;
packageIcon = uiParams.packageInfo != null ? uiParams.packageInfo.packageIcon
: getDeviceAdminIconDrawable(uiParams.deviceAdminIconFilePath);
}
final CustomizationParams customization = uiParams.customization;
mCallback.initializeLayoutParams(
layoutResId,
headerResId,
customization);
setupAcceptAndContinueButton(uiParams.isSilentProvisioning);
mActivity.setTitle(titleResId);
final String headers =
new StringConcatenator(mActivity.getResources()).join(uiParams.disclaimerHeadings);
if (mUtils.isProfileOwnerAction(uiParams.provisioningAction)) {
initiateUIProfileOwner(headers, uiParams.isDeviceManaged, customization,
uiParams.viewTermsIntent);
} else if (mUtils.isDeviceOwnerAction(uiParams.provisioningAction)){
initiateUIDeviceOwner(packageLabel, packageIcon, headers, customization,
uiParams.viewTermsIntent);
}
}
private void setupAcceptAndContinueButton(boolean isSilentProvisioning) {
final GlifLayout layout = mActivity.findViewById(R.id.setup_wizard_layout);
Utils.addAcceptAndContinueButton(layout, v -> onNextButtonClicked());
if (isSilentProvisioning) {
onNextButtonClicked();
}
}
private void onNextButtonClicked() {
ProvisionLogger.logi("Next button (next_button) is clicked.");
mCallback.nextAfterUserConsent();
}
private void initiateUIProfileOwner(String termsHeaders, boolean isComp,
CustomizationParams customizationParams, Intent showTermsIntent) {
// set up the back button
Button backButton = mActivity.findViewById(R.id.back_button);
backButton.setOnClickListener(v -> {
ProvisionLogger.logi("Back button (back_button) is clicked.");
mActivity.onBackPressed();
});
int messageId = isComp ? R.string.profile_owner_info_comp : R.string.profile_owner_info;
int messageWithTermsId = isComp ? R.string.profile_owner_info_with_terms_headers_comp
: R.string.profile_owner_info_with_terms_headers;
// set the short info text
TextView shortInfo = mActivity.findViewById(R.id.profile_owner_short_info);
shortInfo.setText(termsHeaders.isEmpty()
? mActivity.getString(messageId)
: mActivity.getResources().getString(messageWithTermsId, termsHeaders));
// set up show terms button
View viewTermsButton = mActivity.findViewById(R.id.show_terms_button);
viewTermsButton.setOnClickListener(v -> mActivity.startActivity(showTermsIntent));
mTouchTargetEnforcer.enforce(viewTermsButton, (View) viewTermsButton.getParent());
// show the intro animation
mBenefitsAnimation = new BenefitsAnimation(
mActivity,
isComp
? SLIDE_CAPTIONS_COMP
: SLIDE_CAPTIONS,
isComp
? R.string.comp_profile_benefits_description
: R.string.profile_benefits_description,
customizationParams);
}
private void initiateUIDeviceOwner(
String packageName, Drawable packageIcon, String termsHeaders,
CustomizationParams customization, Intent showTermsIntent) {
// set up the cancel button if it exists
View cancelView = mActivity.findViewById(R.id.close_button);
if (cancelView != null) {
cancelView.setOnClickListener(v -> {
ProvisionLogger.logi("Close button (close_button) is clicked.");
mActivity.onBackPressed();
});
}
// short terms info text with clickable 'view terms' link
TextView shortInfoText = mActivity.findViewById(R.id.device_owner_terms_info);
shortInfoText.setText(assembleDOTermsMessage(
termsHeaders, customization.orgName, showTermsIntent));
shortInfoText.setMovementMethod(LinkMovementMethod.getInstance()); // make clicks work
mContextMenuMaker.registerWithActivity(shortInfoText);
handleSupportUrl(customization);
// set up DPC icon and label
setDpcIconAndLabel(packageName, packageIcon, customization.orgName);
}
private void handleSupportUrl(CustomizationParams customization) {
final TextView info = mActivity.findViewById(R.id.device_owner_provider_info);
final String deviceProvider = mActivity.getString(R.string.organization_admin);
final String contactDeviceProvider =
mActivity.getString(R.string.contact_device_provider, deviceProvider);
mUtils.handleSupportUrl(mActivity, customization, mClickableSpanFactory,
mContextMenuMaker, info, deviceProvider, contactDeviceProvider);
}
private Drawable getDeviceAdminIconDrawable(@Nullable String deviceAdminIconFilePath) {
if (deviceAdminIconFilePath == null) {
return null;
}
Bitmap bitmap = BitmapFactory.decodeFile(deviceAdminIconFilePath);
if (bitmap == null) {
return null;
}
return new BitmapDrawable(mActivity.getResources(), bitmap);
}
}