1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.deviceowner;
18 
19 import android.app.Activity;
20 import android.app.Fragment;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ComponentName;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.SharedPreferences;
28 import android.content.pm.ResolveInfo;
29 import android.os.Bundle;
30 import android.provider.Settings;
31 import android.support.annotation.Nullable;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.widget.Button;
36 import android.widget.CompoundButton;
37 import android.widget.SimpleAdapter;
38 import android.widget.Spinner;
39 import android.widget.Switch;
40 
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.List;
44 
45 /**
46  * Demonstrates the usage of the most common device management APIs for the device owner case.
47  * In addition to various features available for profile owners, device owners can perform extra
48  * actions, such as configuring global settings and enforcing a preferred Activity for a specific
49  * IntentFilter.
50  */
51 public class DeviceOwnerFragment extends Fragment {
52 
53     // Keys for SharedPreferences
54     private static final String PREFS_DEVICE_OWNER = "DeviceOwnerFragment";
55     private static final String PREF_LAUNCHER = "launcher";
56 
57     private DevicePolicyManager mDevicePolicyManager;
58 
59     // View references
60     private Switch mSwitchAutoTime;
61     private Switch mSwitchAutoTimeZone;
62     private Spinner mAvailableLaunchers;
63     private Button mButtonLauncher;
64 
65     // Adapter for the spinner to show list of available launchers
66     private LauncherAdapter mAdapter;
67 
68     /**
69      * Handles events on the Switches.
70      */
71     private Switch.OnCheckedChangeListener mOnCheckedChangeListener
72             = new Switch.OnCheckedChangeListener() {
73 
74         @Override
75         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
76             switch (buttonView.getId()) {
77                 case R.id.switch_auto_time:
78                     setBooleanGlobalSetting(Settings.Global.AUTO_TIME, isChecked);
79                     retrieveCurrentSettings(getActivity());
80                     break;
81                 case R.id.switch_auto_time_zone:
82                     setBooleanGlobalSetting(Settings.Global.AUTO_TIME_ZONE, isChecked);
83                     retrieveCurrentSettings(getActivity());
84                     break;
85             }
86         }
87 
88     };
89 
90     /**
91      * Handles click events on the Button.
92      */
93     private View.OnClickListener mOnClickListener
94             = new View.OnClickListener() {
95 
96         @Override
97         public void onClick(View v) {
98             switch (v.getId()) {
99                 case R.id.set_preferred_launcher:
100                     if (loadPersistentPreferredLauncher(getActivity()) == null) {
101                         setPreferredLauncher();
102                     } else {
103                         clearPreferredLauncher();
104                     }
105                     retrieveCurrentSettings(getActivity());
106                     break;
107             }
108         }
109 
110     };
111 
112     /**
113      * @return A newly instantiated {@link DeviceOwnerFragment}.
114      */
newInstance()115     public static DeviceOwnerFragment newInstance() {
116         return new DeviceOwnerFragment();
117     }
118 
119     @Nullable
120     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)121     public View onCreateView(LayoutInflater inflater, ViewGroup container,
122                              Bundle savedInstanceState) {
123         return inflater.inflate(R.layout.fragment_device_owner, container, false);
124     }
125 
126     @Override
onViewCreated(View view, Bundle savedInstanceState)127     public void onViewCreated(View view, Bundle savedInstanceState) {
128         // Retain references
129         mSwitchAutoTime = (Switch) view.findViewById(R.id.switch_auto_time);
130         mSwitchAutoTimeZone = (Switch) view.findViewById(R.id.switch_auto_time_zone);
131         mAvailableLaunchers = (Spinner) view.findViewById(R.id.available_launchers);
132         mButtonLauncher = (Button) view.findViewById(R.id.set_preferred_launcher);
133         // Bind event handlers
134         mSwitchAutoTime.setOnCheckedChangeListener(mOnCheckedChangeListener);
135         mSwitchAutoTimeZone.setOnCheckedChangeListener(mOnCheckedChangeListener);
136         mButtonLauncher.setOnClickListener(mOnClickListener);
137     }
138 
139     @Override
onAttach(Activity activity)140     public void onAttach(Activity activity) {
141         super.onAttach(activity);
142         mDevicePolicyManager =
143                 (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE);
144     }
145 
146     @Override
onDetach()147     public void onDetach() {
148         mDevicePolicyManager = null;
149         super.onDetach();
150     }
151 
152     @Override
onResume()153     public void onResume() {
154         super.onResume();
155         Activity activity = getActivity();
156         if (activity != null) {
157             retrieveCurrentSettings(activity);
158         }
159     }
160 
161     /**
162      * Retrieves the current global settings and changes the UI accordingly.
163      *
164      * @param activity The activity
165      */
retrieveCurrentSettings(Activity activity)166     private void retrieveCurrentSettings(Activity activity) {
167         // Global settings
168         setCheckedSafely(mSwitchAutoTime,
169                 getBooleanGlobalSetting(activity.getContentResolver(), Settings.Global.AUTO_TIME));
170         setCheckedSafely(mSwitchAutoTimeZone,
171                 getBooleanGlobalSetting(activity.getContentResolver(),
172                         Settings.Global.AUTO_TIME_ZONE));
173 
174         // Launcher
175         Intent intent = new Intent(Intent.ACTION_MAIN);
176         intent.addCategory(Intent.CATEGORY_HOME);
177         List<ResolveInfo> list = activity.getPackageManager()
178                 .queryIntentActivities(intent, /* default flags */ 0);
179         mAdapter = new LauncherAdapter(activity, list);
180         mAvailableLaunchers.setAdapter(mAdapter);
181         String packageName = loadPersistentPreferredLauncher(activity);
182         if (packageName == null) { // No preferred launcher is set
183             mAvailableLaunchers.setEnabled(true);
184             mButtonLauncher.setText(R.string.set_as_preferred);
185         } else {
186             int position = -1;
187             for (int i = 0; i < list.size(); ++i) {
188                 if (list.get(i).activityInfo.packageName.equals(packageName)) {
189                     position = i;
190                     break;
191                 }
192             }
193             if (position != -1) {
194                 mAvailableLaunchers.setSelection(position);
195                 mAvailableLaunchers.setEnabled(false);
196                 mButtonLauncher.setText(R.string.clear_preferred);
197             }
198         }
199     }
200 
201     /**
202      * Retrieves the current boolean value of the specified global setting.
203      *
204      * @param resolver The ContentResolver
205      * @param setting  The setting to be retrieved
206      * @return The current boolean value
207      */
getBooleanGlobalSetting(ContentResolver resolver, String setting)208     private static boolean getBooleanGlobalSetting(ContentResolver resolver, String setting) {
209         return 0 != Settings.Global.getInt(resolver, setting, 0);
210     }
211 
212     /**
213      * Sets the boolean value of the specified global setting.
214      *
215      * @param setting The setting to be set
216      * @param value   The value to be set
217      */
setBooleanGlobalSetting(String setting, boolean value)218     private void setBooleanGlobalSetting(String setting, boolean value) {
219         mDevicePolicyManager.setGlobalSetting(
220                 // The ComponentName of the device owner
221                 DeviceOwnerReceiver.getComponentName(getActivity()),
222                 // The settings to be set
223                 setting,
224                 // The value we write here is a string representation for SQLite
225                 value ? "1" : "0");
226     }
227 
228     /**
229      * A utility method to set the checked state of the button without invoking its listener.
230      *
231      * @param button  The button
232      * @param checked The value to be set
233      */
setCheckedSafely(CompoundButton button, boolean checked)234     private void setCheckedSafely(CompoundButton button, boolean checked) {
235         button.setOnCheckedChangeListener(null);
236         button.setChecked(checked);
237         button.setOnCheckedChangeListener(mOnCheckedChangeListener);
238     }
239 
240     /**
241      * Loads the package name from SharedPreferences.
242      *
243      * @param activity The activity
244      * @return The package name of the launcher currently set as preferred, or null if there is no
245      * preferred launcher.
246      */
loadPersistentPreferredLauncher(Activity activity)247     private static String loadPersistentPreferredLauncher(Activity activity) {
248         return activity.getSharedPreferences(PREFS_DEVICE_OWNER, Context.MODE_PRIVATE)
249                 .getString(PREF_LAUNCHER, null);
250     }
251 
252     /**
253      * Saves the package name into SharedPreferences.
254      *
255      * @param activity    The activity
256      * @param packageName The package name to be saved. Pass null to remove the preferred launcher.
257      */
savePersistentPreferredLauncher(Activity activity, String packageName)258     private static void savePersistentPreferredLauncher(Activity activity, String packageName) {
259         SharedPreferences.Editor editor = activity.getSharedPreferences(PREFS_DEVICE_OWNER,
260                 Context.MODE_PRIVATE).edit();
261         if (packageName == null) {
262             editor.remove(PREF_LAUNCHER);
263         } else {
264             editor.putString(PREF_LAUNCHER, packageName);
265         }
266         editor.apply();
267     }
268 
269     /**
270      * Sets the selected launcher as preferred.
271      */
setPreferredLauncher()272     private void setPreferredLauncher() {
273         Activity activity = getActivity();
274         if (activity == null) {
275             return;
276         }
277         IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
278         filter.addCategory(Intent.CATEGORY_HOME);
279         filter.addCategory(Intent.CATEGORY_DEFAULT);
280         ComponentName componentName = mAdapter.getComponentName(
281                 mAvailableLaunchers.getSelectedItemPosition());
282         mDevicePolicyManager.addPersistentPreferredActivity(
283                 DeviceOwnerReceiver.getComponentName(activity), filter, componentName);
284         savePersistentPreferredLauncher(activity, componentName.getPackageName());
285     }
286 
287     /**
288      * Clears the launcher currently set as preferred.
289      */
clearPreferredLauncher()290     private void clearPreferredLauncher() {
291         Activity activity = getActivity();
292         if (activity == null) {
293             return;
294         }
295         mDevicePolicyManager.clearPackagePersistentPreferredActivities(
296                 DeviceOwnerReceiver.getComponentName(activity),
297                 loadPersistentPreferredLauncher(activity));
298         savePersistentPreferredLauncher(activity, null);
299     }
300 
301     /**
302      * Shows list of {@link ResolveInfo} in a {@link Spinner}.
303      */
304     private static class LauncherAdapter extends SimpleAdapter {
305 
306         private static final String KEY_PACKAGE_NAME = "package_name";
307         private static final String KEY_ACTIVITY_NAME = "activity_name";
308 
LauncherAdapter(Context context, List<ResolveInfo> list)309         public LauncherAdapter(Context context, List<ResolveInfo> list) {
310             super(context, createData(list), android.R.layout.simple_list_item_1,
311                     new String[]{KEY_PACKAGE_NAME},
312                     new int[]{android.R.id.text1});
313         }
314 
createData(List<ResolveInfo> list)315         private static List<HashMap<String, String>> createData(List<ResolveInfo> list) {
316             List<HashMap<String, String>> data = new ArrayList<>();
317             for (ResolveInfo info : list) {
318                 HashMap<String, String> map = new HashMap<>();
319                 map.put(KEY_PACKAGE_NAME, info.activityInfo.packageName);
320                 map.put(KEY_ACTIVITY_NAME, info.activityInfo.name);
321                 data.add(map);
322             }
323             return data;
324         }
325 
getComponentName(int position)326         public ComponentName getComponentName(int position) {
327             @SuppressWarnings("unchecked")
328             HashMap<String, String> map = (HashMap<String, String>) getItem(position);
329             return new ComponentName(map.get(KEY_PACKAGE_NAME), map.get(KEY_ACTIVITY_NAME));
330         }
331 
332     }
333 
334 }
335