com.afwsamples.testdpc.SetupManagementFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.afwsamples.testdpc.SetupManagementFragment.java

Source

/*
 * Copyright (C) 2015 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.afwsamples.testdpc;

import android.accounts.Account;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v4.os.BuildCompat;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.afwsamples.testdpc.common.ColorPicker;
import com.afwsamples.testdpc.common.LaunchIntentUtil;
import com.afwsamples.testdpc.common.ProvisioningStateUtil;
import com.afwsamples.testdpc.common.Util;
import com.android.setupwizardlib.SetupWizardLayout;
import com.android.setupwizardlib.view.NavigationBar;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;

import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_MAIN_COLOR;

/**
 * This {@link Fragment} shows the UI that allows the user to start the setup of a managed profile
 * or configuration of a device-owner if the device is in an appropriate state.
 */
public class SetupManagementFragment extends Fragment implements NavigationBar.NavigationBarListener,
        View.OnClickListener, ColorPicker.OnColorSelectListener, RadioGroup.OnCheckedChangeListener {
    // Tag for creating this fragment. This tag can be used to retrieve this fragment.
    public static final String FRAGMENT_TAG = "SetupManagementFragment";

    private static final int REQUEST_PROVISION_MANAGED_PROFILE = 1;
    private static final int REQUEST_PROVISION_DEVICE_OWNER = 2;
    private static final int REQUEST_GET_LOGO = 3;

    private TextView mSetupManagementMessage;
    private RadioGroup mSetupOptions;
    private Button mNavigationNextButton;
    private CheckBox mSkipUserConsent;
    private CheckBox mSkipEncryption;
    private CheckBox mKeepAccountMigrated;
    private ImageButton mParamsIndicator;
    private View mParamsView;
    private static final int[] STATE_EXPANDED = new int[] { R.attr.state_expanded };
    private static final int[] STATE_COLLAPSED = new int[] { -R.attr.state_expanded };

    private ImageView mColorPreviewView;
    private TextView mColorValue;
    private ImageView mLogoPreviewView;
    private TextView mLogoValue;

    private int mCurrentColor;
    private Uri mLogoUri = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mLogoUri = (Uri) savedInstanceState.getParcelable(EXTRA_PROVISIONING_LOGO_URI);
            mCurrentColor = savedInstanceState.getInt(EXTRA_PROVISIONING_MAIN_COLOR);
        } else {
            mLogoUri = resourceToUri(getActivity(), R.drawable.ic_launcher);
            mCurrentColor = getResources().getColor(R.color.teal);
        }

        // Use setupwizard theme
        final Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.SetupTheme);
        LayoutInflater themeInflater = inflater.cloneInContext(contextThemeWrapper);
        View view = themeInflater.inflate(R.layout.setup_management_fragment, container, false);
        SetupWizardLayout layout = (SetupWizardLayout) view.findViewById(R.id.setup_wizard_layout);
        NavigationBar navigationBar = layout.getNavigationBar();
        navigationBar.setNavigationBarListener(this);
        navigationBar.getBackButton().setText(R.string.exit);
        mNavigationNextButton = navigationBar.getNextButton();
        mNavigationNextButton.setText(R.string.setup_label);

        mSetupManagementMessage = (TextView) view.findViewById(R.id.setup_management_message_id);
        mSetupOptions = (RadioGroup) view.findViewById(R.id.setup_options);
        mSetupOptions.setOnCheckedChangeListener(this);
        mSkipUserConsent = (CheckBox) view.findViewById(R.id.skip_user_consent);
        mKeepAccountMigrated = (CheckBox) view.findViewById(R.id.keep_account_migrated);
        mSkipEncryption = (CheckBox) view.findViewById(R.id.skip_encryption);

        mParamsView = view.findViewById(R.id.params);
        mParamsIndicator = (ImageButton) view.findViewById(R.id.params_indicator);
        mParamsIndicator.setOnClickListener(this);

        view.findViewById(R.id.color_select_button).setOnClickListener(this);
        mColorValue = (TextView) view.findViewById(R.id.selected_color_value);
        mColorValue.setText(String.format(ColorPicker.COLOR_STRING_FORMATTER, mCurrentColor));
        mColorPreviewView = (ImageView) view.findViewById(R.id.preview_color);
        mColorPreviewView.setImageTintList(ColorStateList.valueOf(mCurrentColor));

        Intent launchIntent = getActivity().getIntent();
        if (LaunchIntentUtil.isSynchronousAuthLaunch(launchIntent)) {
            Account addedAccount = LaunchIntentUtil.getAddedAccount(launchIntent);
            if (addedAccount != null) {
                view.findViewById(R.id.managed_account_desc).setVisibility(View.VISIBLE);
                // Show the user which account needs management.
                TextView managedAccountName = (TextView) view.findViewById(R.id.managed_account_name);
                managedAccountName.setVisibility(View.VISIBLE);
                managedAccountName.setText(addedAccount.name);
            } else {
                // This is not an expected case, sync-auth is triggered by an account being added so
                // we expect to be told which account to migrate in the launch intent. We don't
                // finish() here as it's still technically feasible to continue.
                Toast.makeText(getActivity(), R.string.invalid_launch_intent_no_account, Toast.LENGTH_LONG).show();
            }
        }
        return view;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putParcelable(EXTRA_PROVISIONING_LOGO_URI, mLogoUri);
        outState.putInt(EXTRA_PROVISIONING_MAIN_COLOR, mCurrentColor);
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onResume() {
        super.onResume();

        getActivity().getActionBar().hide();
        if (setProvisioningMethodsVisibility()) {
            // The extra logo uri and color are supported only from N
            if (BuildCompat.isAtLeastN()) {
                getView().findViewById(R.id.params_title).setVisibility(View.VISIBLE);
                if (canAnAppHandleGetContent()) {
                    getView().findViewById(R.id.choose_logo_item_layout).setVisibility(View.VISIBLE);
                    getView().findViewById(R.id.logo_select_button).setOnClickListener(this);
                    mLogoValue = (TextView) getView().findViewById(R.id.selected_logo_value);
                    mLogoPreviewView = (ImageView) getView().findViewById(R.id.preview_logo);
                }
                setProvisioningModeSpecificUI();
            }
        } else {
            showNoProvisioningPossibleUI();
        }
    }

    /**
     * On R.id.setup_options are changed
     */
    @Override
    public void onCheckedChanged(RadioGroup radioGroup, int i) {
        setProvisioningModeSpecificUI();
    }

    private void setProvisioningModeSpecificUI() {
        final int setUpOptionId = mSetupOptions.getCheckedRadioButtonId();
        final boolean isManagedProfileAction = setUpOptionId == R.id.setup_managed_profile;
        final boolean isManagedDeviceAction = setUpOptionId == R.id.setup_device_owner;
        mSkipUserConsent.setVisibility(
                BuildCompat.isAtLeastO() && isManagedProfileAction && Util.isDeviceOwner(getActivity())
                        ? View.VISIBLE
                        : View.GONE);
        mKeepAccountMigrated
                .setVisibility(BuildCompat.isAtLeastO() && isManagedProfileAction ? View.VISIBLE : View.GONE);
        mSkipEncryption.setVisibility(
                (isManagedProfileAction && BuildCompat.isAtLeastN()) || (isManagedDeviceAction && Util.isAtLeastM())
                        ? View.VISIBLE
                        : View.GONE);

        // If TestDpc is already a device owner, but can create a managed profile, show a different
        // message.
        if (Util.isDeviceOwner(getActivity())) {
            mSetupManagementMessage.setText(R.string.setup_management_message_for_do);
        }
    }

    private void maybeLaunchProvisioning(String intentAction, int requestCode) {
        Activity activity = getActivity();

        Intent intent = new Intent(intentAction);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
                    DeviceAdminReceiver.getComponentName(getActivity()));
        } else {
            intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, getActivity().getPackageName());
        }

        if (!maybeSpecifyNExtras(intent)) {
            // Unable to handle user-input - can't continue.
            return;
        }
        PersistableBundle adminExtras = new PersistableBundle();
        maybeSpecifySyncAuthExtras(intent, adminExtras);
        maybePassAffiliationIds(intent, adminExtras);
        specifySkipUserConsent(intent);
        specifyKeepAccountMigrated(intent);
        specifySkipEncryption(intent);
        specifyDefaultDisclaimers(intent);

        if (adminExtras.size() > 0) {
            intent.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, adminExtras);
        }
        if (intent.resolveActivity(activity.getPackageManager()) != null) {
            startActivityForResult(intent, requestCode);
        } else {
            Toast.makeText(activity, R.string.provisioning_not_supported, Toast.LENGTH_SHORT).show();
        }
    }

    private void maybeSpecifySyncAuthExtras(Intent intent, PersistableBundle adminExtras) {
        Activity activity = getActivity();
        Intent launchIntent = activity.getIntent();

        if (!LaunchIntentUtil.isSynchronousAuthLaunch(launchIntent)) {
            // Don't do anything if this isn't a sync-auth flow.
            return;
        }

        Account accountToMigrate = LaunchIntentUtil.getAddedAccount(launchIntent);
        if (accountToMigrate != null) {
            // EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE only supported in API 22+.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                // Configure the account to migrate into the managed profile if setup
                // completes.
                intent.putExtra(EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, accountToMigrate);
            } else {
                Toast.makeText(activity, R.string.migration_not_supported, Toast.LENGTH_SHORT).show();
            }
        }

        // Perculate launch intent extras through to DeviceAdminReceiver so they can be used there.
        LaunchIntentUtil.prepareDeviceAdminExtras(launchIntent, adminExtras);
    }

    private void maybePassAffiliationIds(Intent intent, PersistableBundle adminExtras) {
        if (Util.isDeviceOwner(getActivity()) && ACTION_PROVISION_MANAGED_PROFILE.equals(intent.getAction())
                && BuildCompat.isAtLeastO()) {
            passAffiliationIds(intent, adminExtras);
        }
    }

    @TargetApi(Build.VERSION_CODES.O)
    private void passAffiliationIds(Intent intent, PersistableBundle adminExtras) {
        ComponentName admin = DeviceAdminReceiver.getComponentName(getActivity());
        DevicePolicyManager dpm = (DevicePolicyManager) getActivity()
                .getSystemService(Context.DEVICE_POLICY_SERVICE);
        List<String> ids = dpm.getAffiliationIds(admin);
        String affiliationId = null;
        if (ids.size() == 0) {
            SecureRandom randomGenerator = new SecureRandom();
            affiliationId = Integer.toString(randomGenerator.nextInt(1000000));
            dpm.setAffiliationIds(admin, Arrays.asList(affiliationId));
        } else {
            affiliationId = ids.get(0);
        }
        adminExtras.putString(LaunchIntentUtil.EXTRA_AFFILIATION_ID, affiliationId);
    }

    /**
     * @return true if we can launch the intent
     */
    private boolean maybeSpecifyNExtras(Intent intent) {
        if (BuildCompat.isAtLeastN()) {
            specifyLogoUri(intent);
            specifyColor(intent);
        }
        return true;
    }

    // TODO: replace with O SDK API
    private static final String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
    private static final String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
    private static final String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";

    private void specifyDefaultDisclaimers(Intent intent) {
        if (BuildCompat.isAtLeastO()) {
            Bundle emmBundle = new Bundle();
            emmBundle.putString(EXTRA_PROVISIONING_DISCLAIMER_HEADER,
                    getString(R.string.default_disclaimer_emm_name));
            emmBundle.putParcelable(EXTRA_PROVISIONING_DISCLAIMER_CONTENT,
                    resourceToUri(getActivity(), R.raw.emm_disclaimer));
            Bundle companyBundle = new Bundle();
            companyBundle.putString(EXTRA_PROVISIONING_DISCLAIMER_HEADER,
                    getString(R.string.default_disclaimer_company_name));
            companyBundle.putParcelable(EXTRA_PROVISIONING_DISCLAIMER_CONTENT,
                    resourceToUri(getActivity(), R.raw.company_disclaimer));
            intent.putExtra(EXTRA_PROVISIONING_DISCLAIMERS, new Bundle[] { emmBundle, companyBundle });
        }
    }

    private static Uri resourceToUri(Context context, int resID) {
        return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
                + context.getResources().getResourcePackageName(resID) + '/'
                + context.getResources().getResourceTypeName(resID) + '/'
                + context.getResources().getResourceEntryName(resID));
    }

    private void specifySkipUserConsent(Intent intent) {
        if (BuildCompat.isAtLeastO() && ACTION_PROVISION_MANAGED_PROFILE.equals(intent.getAction())
                && mSkipUserConsent.getVisibility() == View.VISIBLE) {
            // TODO: use action string in Android SDK
            intent.putExtra("android.app.extra.PROVISIONING_SKIP_USER_CONSENT", mSkipUserConsent.isChecked());
        }
    }

    private void specifyKeepAccountMigrated(Intent intent) {
        if (BuildCompat.isAtLeastO() && ACTION_PROVISION_MANAGED_PROFILE.equals(intent.getAction())
                && mKeepAccountMigrated.getVisibility() == View.VISIBLE) {
            // TODO: use action string in Android SDK
            intent.putExtra("android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION",
                    mKeepAccountMigrated.isChecked());
        }
    }

    private void specifySkipEncryption(Intent intent) {
        if (mSkipEncryption.getVisibility() == View.VISIBLE) {
            intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION, mSkipEncryption.isChecked());
        }
    }

    private void specifyLogoUri(Intent intent) {
        intent.putExtra(EXTRA_PROVISIONING_LOGO_URI, mLogoUri);
        if (mLogoUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setClipData(ClipData.newUri(getActivity().getContentResolver(), "", mLogoUri));
        }
    }

    private void specifyColor(Intent intent) {
        intent.putExtra(EXTRA_PROVISIONING_MAIN_COLOR, mCurrentColor);
    }

    private void showNoProvisioningPossibleUI() {
        mNavigationNextButton.setVisibility(View.GONE);
        TextView textView = (TextView) getView().findViewById(R.id.setup_management_message_id);
        textView.setText(Util.isDeviceOwner(getActivity()) ? R.string.provisioning_not_possible_for_do
                : R.string.provisioning_not_possible);
    }

    /**
     * Set visibility of all provisioning methods
     *
     * @return false if none of the provisioning method is visible
     */
    private boolean setProvisioningMethodsVisibility() {
        boolean hasProvisioningOption = false;
        hasProvisioningOption |= setVisibility(ACTION_PROVISION_MANAGED_PROFILE, R.id.setup_managed_profile);
        hasProvisioningOption |= setVisibility(ACTION_PROVISION_MANAGED_DEVICE, R.id.setup_device_owner);
        return hasProvisioningOption;
    }

    private boolean setVisibility(String action, int radioButtonId) {
        final int visibility = ProvisioningStateUtil.isProvisioningAllowed(getActivity(), action) ? View.VISIBLE
                : View.GONE;
        getView().findViewById(radioButtonId).setVisibility(visibility);
        return visibility == View.VISIBLE;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Activity activity = getActivity();

        switch (requestCode) {
        case REQUEST_PROVISION_MANAGED_PROFILE:
        case REQUEST_PROVISION_DEVICE_OWNER:
            if (resultCode == Activity.RESULT_OK) {
                // Success, finish the enclosing activity. NOTE: Only finish once we're done
                // here, as in synchronous auth cases we don't want the user to return to the
                // Android setup wizard or add-account flow prematurely.
                activity.setResult(Activity.RESULT_OK);
                activity.finish();
            } else {
                // Something went wrong (either provisioning failed, or the user backed out).
                // Let the user decide how to proceed.
                Toast.makeText(activity, R.string.provisioning_failed_or_cancelled, Toast.LENGTH_SHORT).show();
            }
            break;
        case REQUEST_GET_LOGO:
            if (data != null && data.getData() != null) {
                mLogoUri = data.getData();
                mLogoValue.setText(mLogoUri.getLastPathSegment());
                Util.updateImageView(getActivity(), mLogoPreviewView, mLogoUri);
            }
            break;
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        case R.id.params_indicator:
            if (mParamsView.getVisibility() == View.VISIBLE) {
                mParamsView.setVisibility(View.GONE);
                mParamsIndicator.setImageState(STATE_COLLAPSED, false);
            } else {
                mParamsIndicator.setImageState(STATE_EXPANDED, false);
                mParamsView.setVisibility(View.VISIBLE);
            }
            break;
        case R.id.color_select_button:
            ColorPicker.newInstance(mCurrentColor, FRAGMENT_TAG, "provisioningColor").show(getFragmentManager(),
                    "colorPicker");
            break;
        case R.id.logo_select_button:
            startActivityForResult(getGetContentIntent(), REQUEST_GET_LOGO);
            break;
        }
    }

    @Override
    public void onColorSelected(int colorValue, String id) {
        mCurrentColor = colorValue;
        mColorValue.setText(String.format(ColorPicker.COLOR_STRING_FORMATTER, colorValue));
        mColorPreviewView.setImageTintList(ColorStateList.valueOf(colorValue));
    }

    @Override
    public void onNavigateBack() {
        getActivity().onBackPressed();
    }

    @Override
    public void onNavigateNext() {
        if (mSetupOptions.getCheckedRadioButtonId() == R.id.setup_managed_profile) {
            maybeLaunchProvisioning(ACTION_PROVISION_MANAGED_PROFILE, REQUEST_PROVISION_MANAGED_PROFILE);
        } else {
            maybeLaunchProvisioning(ACTION_PROVISION_MANAGED_DEVICE, REQUEST_PROVISION_DEVICE_OWNER);
        }
    }

    private Intent getGetContentIntent() {
        Intent getContent = new Intent(Intent.ACTION_GET_CONTENT);
        getContent.setType("image/*");
        return getContent;
    }

    private boolean canAnAppHandleGetContent() {
        return getGetContentIntent().resolveActivity(getActivity().getPackageManager()) != null;
    }
}