zoukankan      html  css  js  c++  java
  • 如何编程实现开启或关闭GPS(转)

    转自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

    1
    2
    3
    4
    5
    6
    7
    8
    
          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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
            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的代码中用到如下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
      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中的图标按钮的序号。所有的序号在类的首部都有定义:

    1
    2
    3
    4
    5
    
      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中添加如下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    	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,代码片断如下:

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    	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。

  • 相关阅读:
    C++---const
    qt--textEdit多行文本编辑框
    qt--QByteArray字节数组
    qt5--拖放
    qt5--自定义事件与事件的发送
    qt5--键盘事件
    qt5--鼠标事件
    qt5-事件过滤器
    qt5-event事件的传递
    qt-事件的接受和忽略
  • 原文地址:https://www.cnblogs.com/huanglong/p/2117344.html
Copyright © 2011-2022 走看看