转自http://www.learningandroid.net/blog/advance/programmable-toggle-gps/

手机应用中最酷的可能就是位置服务相关的了,如何读取GPS信息,在官方文档上有相当详细的说明,后面如果有机会,我也会专门写例子来介绍(教程已完成,请参见:教程:实现Android的不同精度的定位(基于网络和GPS))。但今天,我们先来看下如何以编程的方式来开启关闭GPS
官方的API中,android.provider.Settings.Secure类有2个静态方法:
public static final void setLocationProviderEnabled (ContentResolver cr, String provider, boolean enabled)

public static final boolean isLocationProviderEnabled (ContentResolver cr, String provider)
不过遗憾的是,这2个方法都注明了从API Level 8(即Android 2.2)才开始提供,那么在2.2之前又该如何编程实现GPS的开关呢?

山重水复疑无路

首先,我们要知道,Android系统的设置画面中就可以进行GPS的开关,那么它是如何实现的呢?
由于我的机器上的android source是2.3版本的,所以直接启动了一个2.1的模拟器,用adb pull将Settings.apk抓下来,反编译之后,在SecuritySettings类中找到如下代码:
package,com.android.settings.SecuritySettings.java

CheckBoxPreference localCheckBoxPreference3 = this.mGps;      if (paramPreference == localCheckBoxPreference3)      {        ContentResolver localContentResolver3 = getContentResolver();        boolean bool6 = this.mGps.isChecked();        Settings.Secure.setLocationProviderEnabled(localContentResolver3, "gps", bool6);        continue;      }

可以看到2.1系统中已经存在有Settings.Secure.setLocationProviderEnabled方法了,只是该方法没有开放而已,事实上读过Android源码的人都对/*hide*/很反感吧,看得到,摸不到!

既然Setting画面中的用法,我们不能使用,那么再换1种方法,我们去看一下Settings.Secure.setLocationProviderEnabled的写法,然后直接套用。
这次,我们直接去看Android 2.3的源码,找到Setting.java之后,找到相关的方法,代码如下:
core, android.provider.Setting.java

public static final void setLocationProviderEnabled(ContentResolver cr,                String provider, boolean enabled) {            // to ensure thread safety, we write the provider name with a '+' or '-'            // and let the SettingsProvider handle it rather than reading and modifying            // the list of enabled providers.            if (enabled) {                provider = "+" + provider;            } else {                provider = "-" + provider;            }            putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);        }
原来这个方法只是1个包装,事实上调用的还是Settings.Secure中的putString方法,我们直接借用过来:
在自己的onClick事件中写上
Settings.Secure.putString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, “network,gps”);
然后执行,WOW,发生了什么,需要android.permission.WRITE_SETTINGS 权限?在Manifest文件中加上,再运行,还是出错,不过这次需要的是android.permission.WRITE_SECURE_SETTINGS,再次加上。
满怀希望的再次运行,结果还是一样的问题:
java.lang.SecurityException: Permission denial: writing to secure settings requires android.permission.WRITE_SECURE_SETTINGS

看来,Google封死了直接调用Settings的路了,事实上我又试着使用反射来直接调用setLocationProviderEnabled方法,结果也是一样的告诉我需要权限

柳暗花明又一村

难道没有别的办法了吗?那些2.1中可以运行的App Widget是如何做到的呢?
再次检视2.1的Settings.apk中的代码,发现有1个widget包,里面有1个类叫SettingsAppWidgetProvider,这就是用来提供App Widget的类,继续研究这里的代码,发现构造RemoteView的代码中用到如下方法:

  private static PendingIntent getLaunchPendingIntent(Context paramContext, int paramInt1, int paramInt2)  {    Intent localIntent1 = new Intent();    Intent localIntent2 = localIntent1.setClass(paramContext, SettingsAppWidgetProvider.class);    Intent localIntent3 = localIntent1.addCategory("android.intent.category.ALTERNATIVE");    Uri localUri = Uri.parse("custom:" + paramInt2);    Intent localIntent4 = localIntent1.setData(localUri);    return PendingIntent.getBroadcast(paramContext, 0, localIntent1, 0);  }

由于这是反编译的结果,略微有点混乱,但还是可以看出思路,目的是通过PendingIntent来扔出1个Intent,接受者是SettingsAppWidgetProvider.class,接受的参数有2个,1个是Category:SettingsAppWidgetProvider.class(正是这个类自身),另1个是Data:Uri.parse(“custom:” + paramInt2),这个paramInt2是Widget中的图标按钮的序号。所有的序号在类的首部都有定义:

 private static final int BUTTON_BLUETOOTH = 4;  private static final int BUTTON_BRIGHTNESS = 1;  private static final int BUTTON_GPS = 3;  private static final int BUTTON_SYNC = 2;  private static final int BUTTON_WIFI = 0;

那么我们只要送出custom:3的Uri给SettingsAppWidgetProvider.class,就应该可以由SettingsAppWidgetProvider类来帮我们调用Settings.Secure.setLocationProviderEnabled方法了。
说到做到,在Activity中添加如下方法:

private void toggleGPS() {Intent gpsIntent = new Intent();gpsIntent.setClassName("com.android.settings","com.android.settings.widget.SettingsAppWidgetProvider");gpsIntent.addCategory("android.intent.category.ALTERNATIVE");gpsIntent.setData(Uri.parse("custom:3"));try {PendingIntent.getBroadcast(this, 0, gpsIntent, 0).send();}catch (CanceledException e) {e.printStackTrace();}}

这个方法是1个纯开关,如果当前是开启的,那么就会关闭它,反之亦然。

检查GPS开关状态

那么,如何查看当前的GPS开关状态呢?可以用上面提到的反射方式调用isLocationProviderEnabled,代码片断如下:

secureClass = cl.loadClass("android.provider.Settings$Secure");isMethod = secureClass.getMethod("isLocationProviderEnabled",ContentResolver.class, String.class);Boolean ret = (Boolean) isMethod.invoke(secureClass, this.getContentResolver(), "gps");

也可以直接用下面的方法:

private void isGPSEnable() {/* 用Setting.System来读取也可以,只是这是更旧的用法String str = Settings.System.getString(getContentResolver(),Settings.Secure.LOCATION_PROVIDERS_ALLOWED);*/String str = Settings.Secure.getString(getContentResolver(),Settings.Secure.LOCATION_PROVIDERS_ALLOWED);Log.v("GPS", str);if (str != null) {return str.contains("gps");}else{return false;}}

这2种方法的原理都是一样的,方法2其实也就是isLocationProviderEnabled实际调用的代码,只是Google未对读取操作进行权限限制。

总结

如果目标手机是运行Android 2.2的话,那么最好还是使用2.2开放的Settings.Secure类中的2个方法来操作。但如果目标手机运行的版本是2.1或以下的话,那么就只能使用变通的方法来实现了。这1方法在Android官方的Wiki上已经有人提出了,详情请见:Issue 7890。但可能是2.1版本已经古旧不再维护的原因,官方并未进行任何的Fix。



更多相关文章

  1. Fix one bug in Android(安卓)官方提供的例子
  2. 关于android中使用new Message的内存泄露问题
  3. Android(安卓)Zip文件解压缩代码
  4. AndFix解析——(上)
  5. 《Android移动应用基础教程》(Android(安卓)Studio)(第二版)黑马教程
  6. Android源码下载——用git clone实现单个目录下载
  7. Android软件广告屏蔽方法及代码
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android 自定义Button按钮显示样式
  2. Android开源项目分类汇总【二】
  3. android group by
  4. Android---Android下嵌入式数据库SqlLite
  5. Android来电和短信操作
  6. Android(安卓)- Design Support Library
  7. android EditText的setCompoundDrawables
  8. AndroidStudio使用GreenDao的方法
  9. Android之上下文菜单(ContextMenu)
  10. 处女男学Android(七)---Android(安卓)应用