/*
* Copyright (C) 2014 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.example.android.basicmanagedprofile;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import static android.app.admin.DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT;
import static android.app.admin.DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED;
/**
* Provides several functions that are available in a managed profile. This includes
* enabling/disabling other apps, setting app restrictions, enabling/disabling intent forwarding,
* and wiping out all the data in the profile.
*/
public class BasicManagedProfileFragment extends Fragment
implements View.OnClickListener,
CompoundButton.OnCheckedChangeListener {
/**
* Tag for logging.
*/
private static final String TAG = "BasicManagedProfileFragment";
/**
* Package name of calculator
*/
private static final String PACKAGE_NAME_CALCULATOR = "com.android.calculator2";
/**
* Package name of Chrome
*/
private static final String PACKAGE_NAME_CHROME = "com.android.chrome";
/**
* {@link Button} to remove this managed profile.
*/
private Button mButtonRemoveProfile;
/**
* Whether the calculator app is enabled in this profile
*/
private boolean mCalculatorEnabled;
/**
* Whether Chrome is enabled in this profile
*/
private boolean mChromeEnabled;
public BasicManagedProfileFragment() {
}
public static BasicManagedProfileFragment newInstance() {
return new BasicManagedProfileFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Retrieves whether the calculator app is enabled in this profile
mCalculatorEnabled = isApplicationEnabled(PACKAGE_NAME_CALCULATOR);
// Retrieves whether Chrome is enabled in this profile
mChromeEnabled = isApplicationEnabled(PACKAGE_NAME_CHROME);
}
/**
* Checks if the application is available in this profile.
*
* @param packageName The package name
* @return True if the application is available in this profile.
*/
private boolean isApplicationEnabled(String packageName) {
Activity activity = getActivity();
PackageManager packageManager = activity.getPackageManager();
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(
packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
// Return false if the app is not installed in this profile
if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) {
return false;
}
// Check if the app is not hidden in this profile
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
return !devicePolicyManager.isApplicationHidden(
BasicDeviceAdminReceiver.getComponentName(activity), packageName);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Bind event listeners and initial states
view.findViewById(R.id.set_chrome_restrictions).setOnClickListener(this);
view.findViewById(R.id.clear_chrome_restrictions).setOnClickListener(this);
view.findViewById(R.id.enable_forwarding).setOnClickListener(this);
view.findViewById(R.id.disable_forwarding).setOnClickListener(this);
view.findViewById(R.id.send_intent).setOnClickListener(this);
mButtonRemoveProfile = (Button) view.findViewById(R.id.remove_profile);
mButtonRemoveProfile.setOnClickListener(this);
Switch toggleCalculator = (Switch) view.findViewById(R.id.toggle_calculator);
toggleCalculator.setChecked(mCalculatorEnabled);
toggleCalculator.setOnCheckedChangeListener(this);
Switch toggleChrome = (Switch) view.findViewById(R.id.toggle_chrome);
toggleChrome.setChecked(mChromeEnabled);
toggleChrome.setOnCheckedChangeListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.set_chrome_restrictions: {
setChromeRestrictions();
break;
}
case R.id.clear_chrome_restrictions: {
clearChromeRestrictions();
break;
}
case R.id.enable_forwarding: {
enableForwarding();
break;
}
case R.id.disable_forwarding: {
disableForwarding();
break;
}
case R.id.send_intent: {
sendIntent();
break;
}
case R.id.remove_profile: {
mButtonRemoveProfile.setEnabled(false);
removeProfile();
break;
}
}
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
switch (compoundButton.getId()) {
case R.id.toggle_calculator: {
setAppEnabled(PACKAGE_NAME_CALCULATOR, checked);
mCalculatorEnabled = checked;
break;
}
case R.id.toggle_chrome: {
setAppEnabled(PACKAGE_NAME_CHROME, checked);
mChromeEnabled = checked;
break;
}
}
}
/**
* Enables or disables the specified app in this profile.
*
* @param packageName The package name of the target app.
* @param enabled Pass true to enable the app.
*/
private void setAppEnabled(String packageName, boolean enabled) {
Activity activity = getActivity();
if (null == activity) {
return;
}
PackageManager packageManager = activity.getPackageManager();
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
// Here, we check the ApplicationInfo of the target app, and see if the flags have
// ApplicationInfo.FLAG_INSTALLED turned on using bitwise operation.
if (0 == (applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)) {
// If the app is not installed in this profile, we can enable it by
// DPM.enableSystemApp
if (enabled) {
devicePolicyManager.enableSystemApp(
BasicDeviceAdminReceiver.getComponentName(activity), packageName);
} else {
// But we cannot disable the app since it is already disabled
Log.e(TAG, "Cannot disable this app: " + packageName);
return;
}
} else {
// If the app is already installed, we can enable or disable it by
// DPM.setApplicationHidden
devicePolicyManager.setApplicationHidden(
BasicDeviceAdminReceiver.getComponentName(activity), packageName, !enabled);
}
Toast.makeText(activity, enabled ? R.string.enabled : R.string.disabled,
Toast.LENGTH_SHORT).show();
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "The app cannot be found: " + packageName, e);
}
}
/**
* Sets restrictions to Chrome
*/
private void setChromeRestrictions() {
final Activity activity = getActivity();
if (null == activity) {
return;
}
final DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
final Bundle settings = new Bundle();
settings.putString("EditBookmarksEnabled", "false");
settings.putString("IncognitoModeAvailability", "1");
settings.putString("ManagedBookmarks",
"[{\"name\": \"Chromium\", \"url\": \"http://chromium.org\"}, " +
"{\"name\": \"Google\", \"url\": \"https://www.google.com\"}]");
settings.putString("DefaultSearchProviderEnabled", "true");
settings.putString("DefaultSearchProviderName", "\"LMGTFY\"");
settings.putString("DefaultSearchProviderSearchURL",
"\"http://lmgtfy.com/?q={searchTerms}\"");
settings.putString("URLBlacklist", "[\"example.com\", \"example.org\"]");
StringBuilder message = new StringBuilder("Setting Chrome restrictions:");
for (String key : settings.keySet()) {
message.append("\n");
message.append(key);
message.append(": ");
message.append(settings.getString(key));
}
ScrollView view = new ScrollView(activity);
TextView text = new TextView(activity);
text.setText(message);
int size = (int) activity.getResources().getDimension(R.dimen.activity_horizontal_margin);
view.setPadding(size, size, size, size);
view.addView(text);
new AlertDialog.Builder(activity)
.setView(view)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// This is how you can set restrictions to an app.
// The format for settings in Bundle differs from app to app.
manager.setApplicationRestrictions
(BasicDeviceAdminReceiver.getComponentName(activity),
PACKAGE_NAME_CHROME, settings);
Toast.makeText(activity, R.string.restrictions_set,
Toast.LENGTH_SHORT).show();
}
})
.show();
}
/**
* Clears restrictions to Chrome
*/
private void clearChromeRestrictions() {
final Activity activity = getActivity();
if (null == activity) {
return;
}
final DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
// In order to clear restrictions, pass null as the restriction Bundle for
// setApplicationRestrictions
manager.setApplicationRestrictions
(BasicDeviceAdminReceiver.getComponentName(activity),
PACKAGE_NAME_CHROME, null);
Toast.makeText(activity, R.string.cleared, Toast.LENGTH_SHORT).show();
}
/**
* Enables forwarding of share intent between private account and managed profile.
*/
private void enableForwarding() {
Activity activity = getActivity();
if (null == activity || activity.isFinishing()) {
return;
}
DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
try {
IntentFilter filter = new IntentFilter(Intent.ACTION_SEND);
filter.addDataType("text/plain");
filter.addDataType("image/jpeg");
// This is how you can register an IntentFilter as allowed pattern of Intent forwarding
manager.addCrossProfileIntentFilter(BasicDeviceAdminReceiver.getComponentName(activity),
filter, FLAG_MANAGED_CAN_ACCESS_PARENT | FLAG_PARENT_CAN_ACCESS_MANAGED);
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
}
/**
* Disables forwarding of all intents.
*/
private void disableForwarding() {
Activity activity = getActivity();
if (null == activity || activity.isFinishing()) {
return;
}
DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.clearCrossProfileIntentFilters(BasicDeviceAdminReceiver.getComponentName(activity));
}
/**
* Sends a sample intent of a plain text message. This is just a utility function to see how
* the intent forwarding works.
*/
private void sendIntent() {
Activity activity = getActivity();
if (null == activity || activity.isFinishing()) {
return;
}
DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT,
manager.isProfileOwnerApp(activity.getApplicationContext().getPackageName())
? "From the managed account" : "From the primary account");
try {
startActivity(intent);
Log.d(TAG, "A sample intent was sent.");
} catch (ActivityNotFoundException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
}
}
/**
* Wipes out all the data related to this managed profile.
*/
private void removeProfile() {
Activity activity = getActivity();
if (null == activity || activity.isFinishing()) {
return;
}
DevicePolicyManager manager =
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.wipeData(0);
// The screen turns off here
}
}