Home | History | Annotate | Download | only in system
      1 /*
      2  * Copyright (C) 2014 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.android.tv.settings.system;
     18 
     19 import com.android.tv.settings.R;
     20 
     21 import com.android.tv.settings.ActionBehavior;
     22 import com.android.tv.settings.ActionKey;
     23 import com.android.tv.settings.BaseSettingsActivity;
     24 import android.accounts.Account;
     25 import android.accounts.AccountManager;
     26 import com.android.tv.settings.util.SettingsHelper;
     27 import com.android.tv.settings.dialog.old.Action;
     28 import com.android.tv.settings.dialog.old.ActionAdapter;
     29 import com.android.tv.settings.device.apps.AppManagementActivity;
     30 
     31 import android.app.AppOpsManager;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.content.pm.ApplicationInfo;
     35 import android.content.pm.PackageManager;
     36 import android.location.LocationManager;
     37 import android.os.Bundle;
     38 import android.preference.Preference;
     39 import android.provider.Settings;
     40 import android.util.Log;
     41 import java.util.ArrayList;
     42 import java.util.HashMap;
     43 import java.util.List;
     44 
     45 /**
     46  * Controls location settings.
     47  */
     48 public class LocationActivity extends BaseSettingsActivity implements ActionAdapter.Listener {
     49 
     50     private static final String TAG = "LocationActivity";
     51     private static final boolean DEBUG = false;
     52 
     53     /**
     54      * Stores a BatterySipper object and records whether the sipper has been
     55      * used.
     56      */
     57     private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
     58     /**
     59      * Package name of GmsCore
     60      */
     61     private static final String GMS_PACKAGE = "com.google.android.gms";
     62     /**
     63      * Class name of Google location settings
     64      */
     65     private static final String GOOGLE_LOCATION_SETTINGS_CLASS =
     66             "com.google.android.location.settings.GoogleLocationSettingsActivity";
     67     /**
     68      * Account type for google accounts
     69      */
     70     private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
     71 
     72     /**
     73      * The extra key whose value specifies the name of account to be operated
     74      * on.
     75      */
     76     static final String EXTRA_KEY_ACCOUNT = "com.google.android.location.settings.extra.account";
     77 
     78     private SettingsHelper mHelper;
     79     private String mAccountName = null;
     80 
     81     @Override
     82     protected void onCreate(Bundle savedInstanceState) {
     83         mHelper = new SettingsHelper(getApplicationContext());
     84 
     85         super.onCreate(savedInstanceState);
     86     }
     87 
     88     @Override
     89     protected void onPause() {
     90         super.onPause();
     91     }
     92 
     93     @Override
     94     protected void onDestroy() {
     95         super.onDestroy();
     96     }
     97 
     98     @Override
     99     protected Object getInitialState() {
    100         return ActionType.LOCATION_OVERVIEW;
    101     }
    102 
    103     @Override
    104     protected void refreshActionList() {
    105         mActions.clear();
    106         switch ((ActionType) mState) {
    107             case LOCATION_OVERVIEW:
    108                 mActions.add(ActionType.LOCATION_STATUS.toAction(
    109                         mResources, mHelper.getStatusStringFromBoolean(isLocationEnabled())));
    110                 if (isLocationEnabled()) {
    111                     mActions.add(ActionType.LOCATION_MODE.toAction(mResources,
    112                             getString(R.string.location_mode_wifi_description), false));
    113                     mActions.add(ActionType.LOCATION_RECENT_REQUESTS.toAction(mResources));
    114                 }
    115                 break;
    116             case LOCATION_STATUS:
    117                 Action locationStatusOn = ActionType.ON.toAction(mResources);
    118                 locationStatusOn.setChecked(isLocationEnabled());
    119                 Action locationStatusOff = ActionType.OFF.toAction(mResources);
    120                 locationStatusOff.setChecked(!isLocationEnabled());
    121                 mActions.add(locationStatusOn);
    122                 mActions.add(locationStatusOff);
    123                 break;
    124             case LOCATION_RECENT_REQUESTS:
    125                 mActions = getRecentRequestActions();
    126                 if (mActions.size() == 0) {
    127                     mActions.add(ActionType.LOCATION_NO_RECENT_REQUESTS.toAction(
    128                             mResources, false));
    129                 }
    130                 break;
    131             case LOCATION_SERVICES:
    132                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE.toAction(mResources));
    133                 break;
    134             case LOCATION_SERVICES_GOOGLE:
    135                 mActions = getAccountsActions();
    136                 break;
    137             case LOCATION_SERVICES_GOOGLE_SETTINGS:
    138                 // TODO add on and off
    139                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE_REPORTING.toAction(mResources));
    140                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE_HISTORY.toAction(mResources));
    141                 break;
    142             case LOCATION_SERVICES_GOOGLE_REPORTING:
    143             case LOCATION_SERVICES_GOOGLE_HISTORY:
    144                 // TODO set checked here
    145                 mActions.add(ActionType.ON.toAction(mResources));
    146                 mActions.add(ActionType.OFF.toAction(mResources));
    147                 break;
    148             default:
    149         }
    150     }
    151 
    152     // TODO Remove this. Use our own UI
    153     private void startHoloGoogleLocationServicesSettings() {
    154         Intent i = new Intent();
    155         i.setClassName(GMS_PACKAGE, GOOGLE_LOCATION_SETTINGS_CLASS);
    156         startActivity(i);
    157     }
    158 
    159     private ArrayList<Action> getAccountsActions(){
    160         ArrayList<Action> result = new ArrayList<Action>();
    161         Action.Builder builder = new Action.Builder();
    162         Account[] googleAccounts = ((AccountManager) getSystemService(Context.ACCOUNT_SERVICE))
    163                 .getAccountsByType(ACCOUNT_TYPE_GOOGLE);
    164         for (Account account : googleAccounts) {
    165             result.add(builder.key(account.name).title(account.name).build());
    166         }
    167         return result;
    168     }
    169 
    170     /**
    171      * Fills a list of applications which queried location recently within
    172      * specified time. TODO: add icons
    173      */
    174     private ArrayList<Action> getRecentRequestActions() {
    175         ArrayList<Action> result = new ArrayList<Action>();
    176 
    177         // Retrieve a location usage list from AppOps
    178         AppOpsManager aoManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
    179         List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
    180                 new int[] {
    181                         AppOpsManager.OP_MONITOR_LOCATION,
    182                         AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
    183                 });
    184         long now = System.currentTimeMillis();
    185         for (AppOpsManager.PackageOps ops : appOps) {
    186             Action action = getActionFromOps(now, ops);
    187             if (action != null) {
    188                 result.add(action);
    189             }
    190         }
    191 
    192         return result;
    193     }
    194 
    195     private Action getActionFromOps(long now, AppOpsManager.PackageOps ops) {
    196         String packageName = ops.getPackageName();
    197         List<AppOpsManager.OpEntry> entries = ops.getOps();
    198         boolean highBattery = false;
    199         boolean normalBattery = false;
    200 
    201         // Earliest time for a location request to end and still be shown in
    202         // list.
    203         long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
    204         for (AppOpsManager.OpEntry entry : entries) {
    205             if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
    206                 switch (entry.getOp()) {
    207                     case AppOpsManager.OP_MONITOR_LOCATION:
    208                         normalBattery = true;
    209                         break;
    210                     case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
    211                         highBattery = true;
    212                         break;
    213                     default:
    214                         break;
    215                 }
    216             }
    217         }
    218 
    219         if (!highBattery && !normalBattery) {
    220             if (DEBUG) {
    221                 Log.v(TAG, packageName + " hadn't used location within the time interval.");
    222             }
    223             return null;
    224         }
    225 
    226         Action.Builder builder = new Action.Builder();
    227         // The package is fresh enough, continue.
    228         try {
    229             ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
    230                     packageName, PackageManager.GET_META_DATA);
    231             if (appInfo.uid == ops.getUid()) {
    232                 builder.key(packageName)
    233                         .title(getPackageManager().getApplicationLabel(appInfo).toString())
    234                         .description(highBattery ? getString(R.string.location_high_battery_use)
    235                                 : getString(R.string.location_low_battery_use));
    236             } else if (DEBUG) {
    237                 Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() +
    238                         " belongs to another inactive account, ignored.");
    239             }
    240         } catch (PackageManager.NameNotFoundException e) {
    241             Log.wtf(TAG, "Package not found: " + packageName);
    242         }
    243         return builder.build();
    244     }
    245 
    246     private boolean isLocationEnabled() {
    247         return Settings.Secure.getInt(getContentResolver(),
    248                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
    249                 Settings.Secure.LOCATION_MODE_OFF;
    250     }
    251 
    252     @Override
    253     protected void updateView() {
    254         refreshActionList();
    255         switch ((ActionType) mState) {
    256             case LOCATION_OVERVIEW:
    257                 setView(R.string.system_location, R.string.settings_app_name,
    258                         R.string.system_desc_location, R.drawable.ic_settings_location);
    259                 break;
    260             case LOCATION_SERVICES_GOOGLE_SETTINGS:
    261                 setView(mAccountName, getPrevState() != null ?
    262                         ((ActionType) getPrevState()).getTitle(mResources) : null,
    263                         ((ActionType) mState).getDescription(mResources), 0);
    264                 break;
    265             case ON:
    266             case OFF:
    267             case LOCATION_SERVICES_GOOGLE_REPORTING:
    268             case LOCATION_SERVICES_GOOGLE_HISTORY:
    269                 break;
    270             default:
    271                 setView(((ActionType) mState).getTitle(mResources), getPrevState() != null ?
    272                         ((ActionType) getPrevState()).getTitle(mResources) : null,
    273                         ((ActionType) mState).getDescription(mResources), 0);
    274                 break;
    275         }
    276     }
    277 
    278     @Override
    279     public void onActionClicked(Action action) {
    280         // clicking on recent location access apps
    281         switch ((ActionType) mState) {
    282             case LOCATION_RECENT_REQUESTS:
    283                 // TODO handle no recent apps
    284                 String packageName = action.getKey();
    285                 Intent i = AppManagementActivity.getLaunchIntent(packageName);
    286                 startActivity(i);
    287                 return;
    288 
    289             case LOCATION_SERVICES_GOOGLE:
    290                 mAccountName = action.getTitle();
    291                 setState(ActionType.LOCATION_SERVICES_GOOGLE_SETTINGS, true);
    292                 return;
    293             default:
    294         }
    295 
    296         ActionKey<ActionType, ActionBehavior> actionKey = new ActionKey<ActionType, ActionBehavior>(
    297                 ActionType.class, ActionBehavior.class, action.getKey());
    298         final ActionType type = actionKey.getType();
    299         switch (type) {
    300             case ON:
    301                 setProperty(true);
    302                 goBack();
    303                 break;
    304             case OFF:
    305                 setProperty(false);
    306                 goBack();
    307                 break;
    308             case LOCATION_SERVICES_GOOGLE_REPORTING:
    309                 startLocationReportingSettings();
    310                 break;
    311             case LOCATION_SERVICES_GOOGLE_HISTORY:
    312                 startLocationHistorySettings();
    313                 break;
    314             case LOCATION_SERVICES_GOOGLE: // TODO remove this here, it should
    315                                            // fall into the default once we
    316                                            // figure out how to now use the
    317                                            // settings in GmsCore
    318                 startHoloGoogleLocationServicesSettings();
    319                 break;
    320             default:
    321                 setState(type, true);
    322                 break;
    323         }
    324     }
    325 
    326     @Override
    327     protected void setProperty(boolean enable) {
    328         switch ((ActionType) mState) {
    329             case LOCATION_STATUS:
    330                 setLocationMode(enable);
    331                 break;
    332              default:
    333         }
    334     }
    335 
    336     // TODO Location history settings is currently in
    337     // com.google.android.location.settings.LocationHistorySettingsActivity and
    338     // is non exported
    339     private void startLocationHistorySettings() {
    340     }
    341 
    342     // TODO Location reporting settings is currently in
    343     // com.google.android.location.settings.LocationReportingSettingsActivity
    344     // and is non exported
    345     private void startLocationReportingSettings() {
    346     }
    347 
    348     private void setLocationMode(boolean enable) {
    349         if (enable) {
    350             // TODO
    351             // com.google.android.gms/com.google.android.location.network.ConfirmAlertActivity
    352             // pops up when we turn this on.
    353             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
    354                     Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
    355         } else {
    356             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
    357                     Settings.Secure.LOCATION_MODE_OFF);
    358         }
    359     }
    360 }
    361