GPS代码学习 --- 设置菜单

一、GPS在设置中的代码。

        节前看了一小部分GPS设置部分代码,对应手机菜单中“设置\位置信息访问权限”,参考代码LocationSettings.java, AgpsEpoSettings.java, CustomSwitchPreference.java。
该设置菜单使用SettingsPreferenceFragment(继承自preferenceFragment)实现,设置菜单在onResume中通过createPreferenceHierarchy加载布局,并对部分控件设置监听。
onPreferenceTreeClick实现菜单的点击响应,onPreferenceChange响应之前设置的CheckboxPreference控件(允许访问我的位置信息)的勾选处理。

在GPS卫星定位这个选择开关菜单的处理上,MTK使用自定义的CustomSwitchPreference来实现开关,和android framework中定义的SwitchPreference类似。
差别在SwitchPreference类中注册了一个CompoundButton的OnCheckedChangeListener,在该监听中会调用一个callChangeListener的API。

调用该API和不调用API有何差别?从callChangeListener函数实现来看,

    protected boolean callChangeListener(Object newValue) {
        return mOnChangeListener == null ? true : mOnChangeListener.onPreferenceChange(this, newValue);
    }

原来它会调用mOnChangeListener.onPreferenceChange,也就是为什么我们之前需要在onResume中注册对CheckboxPreference的监听了,
mLocationAccess.setOnPreferenceChangeListener(this);
         那如果我们想要让MTK自己设计的CustomSwitchPreference控件也能像mLocationAccess一样在点击后响应onPreferenceChange,那我们就必须先增加监听,
         mGps.setOnPreferenceChangeListener(this);
然后,关键要在CustomSwitchPreference的监听CompoundButton的Listener中实现onCheckedChange,

private class Listener implements CompoundButton.OnCheckedChangeListener {
    		@Override
   		 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        			Settings.Secure.setLocationProviderEnabled(buttonView.getContext().getContentResolver(),
                			LocationManager.GPS_PROVIDER, isChecked);
        			setChecked(isChecked);
        			// Joseph fixed.
        			if (isChecked) {
            			boolean value = callChangeListener(isChecked);
            			Log.d("CustomSwitchPreference", "setChecked: value = " + value);
        			}
        			// Joseph fixed end.
        			return;
    		}
}

然后,就可以在LocationSettings.java的onPreferenceChange函数中增加对控件Gps的状态改变处理,例如:

    @Override
    public boolean onPreferenceChange(Preference pref, Object newValue) {
        ......
        else if (pref.getKey().equals(KEY_LOCATION_GPS)) { // Joseph fixed.
            if (getAgpsEnable()) {
                showDialog(CONFIRM_AGPS_DIALOG_ID);
            }
        } // Joseph fixed end.
        ......
    }

二、GPS在widget中的代码。
       先说说widget上的。代码可参考SettingsAppWidgetProvider.java, 和一般widget的实现一样,该类继承自AppWidgetProvider。说白了就是一个BroadcastReceiver,呵呵~
如何实现一个widget就不在这里展开了。在SettingsAppWidgetProvider中实现了wifi,BT,GPS,Sync,Brightness这几个按钮的状态Tracker,这几个按钮大致的响应动作是类似的,所以有了一个abstract class StateTracker,这个模式让我想到了设计模式中的State模式。


        StateTracker中有虚函数getContainerId(), getButtonId(), getIndicatorId(), getButtonImageId()等,用于更换显示对应的图标按钮等。在onReceive()中会收到两类消息,一类是LocationManager.PROVIDERS_CHANGED_ACTION,一类是intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)。前者处理来自设置菜单功能开关消息,后者处理待机widget点击button的事件。以GPS按钮的点击事件处理来讲,在SettingsAppWidgetProvider的onUpdate()中调用了buildUpdate,代码如下:

static RemoteViews buildUpdate(Context context) {
    ......
        if (FeatureOption.MTK_GPS_SUPPORT) {
        views.setOnClickPendingIntent(R.id.btn_gps,
                getLaunchPendingIntent(context, BUTTON_GPS));
        } else {
            views.setViewVisibility(R.id.btn_gps, View.GONE);
        }
    ......
}

        Views是RemoteViews对象,android官网介绍setOnClickPendingIntent()如下:
Equivalent to calling setOnClickListener(android.view.View.OnClickListener) to launch the provided PendingIntent.等价于调用setOnClickListener进入对应的PendingIntent。也就是说点击btn_gps,会发送intent给SettingsAppWidgetProvider,传入的参数有ButtonID:BUTTON_GPS,category是Intent.CATEGORY_ALTERNATIVE。代码如下:

private static PendingIntent getLaunchPendingIntent(Context context,
            int buttonId) {
        Intent launchIntent = new Intent();
        launchIntent.setClass(context, SettingsAppWidgetProvider.class);
        launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
        launchIntent.setData(Uri.parse("custom:" + buttonId));
        PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* no requestCode */,
                launchIntent, 0 /* no flags */);
        Log.d(TAG, "PendingIntent , buttonId = " + buttonId + " pi = " + pi);
        return pi;
}

        所以点击按钮BUTTON_GPS后,SettingsAppWidgetProvider会收到消息并响应toggleState。

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        ......
        else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
            Uri data = intent.getData();
            int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
            Log.d(TAG, "onReceive , toggleState");
            ......
            } else if (FeatureOption.MTK_GPS_SUPPORT && (buttonId == BUTTON_GPS)){
                Log.d(TAG, "onReceive , sGpsState.toggleState");
                sGpsState.toggleState(context);
            }
            ......
        }
    ......
    }

        toggleState在判断当前按钮未在转换过程中,会调用对应GpsStateTracker的requestStateChange()。在其中运用了AsyncTask来处理。简单介绍下AsyncTask:
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.
AsyncTask设计为一个围绕Thread和Handler的辅助类,用于简短的操作。它会执行以下4个步骤:
1、onPreExecute(), 在task执行前被UI线程调用,往往用来设置task,比如显示进度条。
2、doInBackground(Params...),在onPreExecute()完成执行后,马上会被后台线程调用。该步骤用来完成费时的后台计算,计算结果返回并传递给最后一个步骤。这些值会在UI线程打印在onProgressUpdate步骤。
3、onProgressUpdate(Progress...),该方法用来在后台数据在计算过程中显示进度,执行时间不定,在UI线程中由publishProgress调用。
4、onPostExecute(Result),在后台计算完成后,由UI线程调用,参数为后台计算的返回值。
在GPS按钮点击后,在doInBackground()中设置状态后,在onPostExecute中得到设置的状态,并调用updateWidget(context)来刷新widget。


三、GPS在SystemUI中的代码。
        再讲systemUI中的GPS按钮。对应代码在QuickSettingsConnectionModel.java,QuickSettings.java。与在SettingsAppWidgetProvider.java中实现类似,GPS,wifi,BT,AirlineMode,mobileState等也都继承自StateTracker(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/util/StateTracker.java)。同样在QucikSetting中调用setupQuickSettings(),成员变量mQuickSettingsConnectionModel调用buildIconViews,在其中设置mGpsStateTracker以及对应view的onClickListener。在点击view后,触发mGpsStateTracker的toggleState,类似在widget中的处理,在toggleState调用requestStateChange后用了AsyncTask实现状态的更新。

        @Override
        public void requestStateChange(final Context context, final boolean desiredState) {
            final ContentResolver resolver = context.getContentResolver();
            new AsyncTask<Void, Void, Boolean>() {
                @Override
                protected Boolean doInBackground(Void... args) {
                    Settings.Secure.setLocationProviderEnabled(resolver, LocationManager.GPS_PROVIDER, desiredState);
                    return desiredState;
                }
                @Override
                protected void onPostExecute(Boolean result) {
                    setCurrentState(context, result ? STATE_ENABLED : STATE_DISABLED);
                    setImageViewResources(context);
                }
            }.execute();
        }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值