home键在KeyEvent中的键值为3.
public static final int KEYCODE_HOME = 3;
当用户按下home键的时候(包括长按),程序会进入到PhoneWindowManager.java类中的public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags)这个方法中进行处理。如果用户是连续点击home,此时就要执行长按home事件了。即执行mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());对应的代码。也就会跳转到mHomeLongPress这个Runnable接着往下执行。
interceptKeyBeforeDispatching这个方法位于PhoneWindowManager.java中。位置为:\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java
- public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
- int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
- final boolean down = (action == KeyEvent.ACTION_DOWN);
- ...
-
- if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) {
- mHandler.removeCallbacks(mHomeLongPress);
- }
-
- if (mHomePressed) {
- if (keyCode == KeyEvent.KEYCODE_HOME) {
-
-
- if (!down) {
- mHomePressed = false;
- if (!canceled) {
- boolean incomingRinging = false;
- try {
- ITelephony telephonyService = getTelephonyService();
- if (telephonyService != null) {
- incomingRinging = telephonyService.isRinging();
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
- }
-
- if (incomingRinging) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- } else {
-
- launchHomeFromHotKey();
- }
- } else {
- Log.i(TAG, "Ignoring HOME; event canceled.");
- }
- }
- }
- return true;
- }
-
- ...
-
-
- if (keyCode == KeyEvent.KEYCODE_HOME) {
-
-
-
- WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
- if (attrs != null) {
- final int type = attrs.type;
- if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
- || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
-
- return false;
- }
- final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
- for (int i=0; i<typeCount; i++) {
- if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
-
- return true;
- }
- }
- }
-
-
- if (down && repeatCount == 0) {
- if (!keyguardOn) {
- mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
- }
- mHomePressed = true;
- }
- return true;
- }
- else if(...){...}
mHomeLongPress的代码如下。它的作用是请求生成一个弹出近期任务的对话框。执行showRecentAppsDialog()方法
-
-
-
- Runnable mHomeLongPress = new Runnable() {
- public void run() {
-
-
-
-
- mHomePressed = false;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
- sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
- showRecentAppsDialog();
- }
- };
showRecentAppsDialog()方法如下。它会去创建一个RecentApplicationsDialog对象,从而完成弹出任务对话框的操作。
-
-
-
- void showRecentAppsDialog() {
- if (mRecentAppsDialog == null) {
- mRecentAppsDialog = new RecentApplicationsDialog(mContext);
- }
- mRecentAppsDialog.show();
- }
接下来就是创建RecentApplicationsDialog对象来显示近期任务了。该类位置为:\frameworks\base\policy\src\com\android\internal\policy\impl\RecentApplicationsDialog.java
代码中已经做了详细的注解,这里就不再仔细说。最重要的一步就是在onStart()中的reloadButtons()方法。它是获取近期任务的方法。
- public class RecentApplicationsDialog extends Dialog implements OnClickListener {
-
- private static final boolean DBG_FORCE_EMPTY_LIST = false;
- static private StatusBarManager sStatusBar;
- private static final int NUM_BUTTONS = 8;
- private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2;
- final TextView[] mIcons = new TextView[NUM_BUTTONS];
- View mNoAppsText;
- IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- Handler mHandler = new Handler();
-
- Runnable mCleanup = new Runnable() {
- public void run() {
-
- for (TextView icon: mIcons) {
- icon.setCompoundDrawables(null, null, null, null);
- icon.setTag(null);
- }
- }
- };
-
- private int mIconSize;
-
- public RecentApplicationsDialog(Context context) {
- super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
-
- final Resources resources = context.getResources();
- mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size);
- }
-
-
-
-
-
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Context context = getContext();
-
-
-
- if (sStatusBar == null) {
- sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
- }
-
-
- Window window = getWindow();
- window.requestFeature(Window.FEATURE_NO_TITLE);
- window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
- window.setTitle("Recents");
-
- setContentView(com.android.internal.R.layout.recent_apps_dialog);
- final WindowManager.LayoutParams params = window.getAttributes();
- params.width = WindowManager.LayoutParams.MATCH_PARENT;
- params.height = WindowManager.LayoutParams.MATCH_PARENT;
- window.setAttributes(params);
- window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
-
- mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);
- mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);
- mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);
- mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);
- mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);
- mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);
- mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);
- mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7);
- mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
-
-
- for (TextView b: mIcons) {
- b.setOnClickListener(this);
- }
- }
-
-
-
-
- public void onClick(View v) {
-
- for (TextView b: mIcons) {
- if (b == v) {
-
-
- Intent intent = (Intent)b.getTag();
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- try {
- getContext().startActivity(intent);
- } catch (ActivityNotFoundException e) {
- Log.w("Recent", "Unable to launch recent task", e);
- }
- }
- break;
- }
- }
- dismiss();
- }
-
-
-
-
-
- @Override
- public void onStart() {
- super.onStart();
- reloadButtons();
- if (sStatusBar != null) {
- sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
- }
-
-
-
- getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter);
- mHandler.removeCallbacks(mCleanup);
- }
-
-
-
-
- @Override
- public void onStop() {
- super.onStop();
-
- if (sStatusBar != null) {
- sStatusBar.disable(StatusBarManager.DISABLE_NONE);
- }
-
-
-
-
- getContext().unregisterReceiver(mBroadcastReceiver);
-
- mHandler.postDelayed(mCleanup, 100);
- }
-
-
-
-
-
- private void reloadButtons() {
-
- final Context context = getContext();
- final PackageManager pm = context.getPackageManager();
- final ActivityManager am = (ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
- final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
-
- ActivityInfo homeInfo =
- new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm, 0);
-
-
-
- IconUtilities iconUtilities = new IconUtilities(getContext());
-
-
-
-
- int index = 0;
- int numTasks = recentTasks.size();
-
-
- for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
- final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
-
-
- if (DBG_FORCE_EMPTY_LIST && (i == 0)) continue;
-
- Intent intent = new Intent(info.baseIntent);
- if (info.origActivity != null) {
- intent.setComponent(info.origActivity);
- }
-
-
-
- if (homeInfo != null) {
- if (homeInfo.packageName.equals(
- intent.getComponent().getPackageName())
- && homeInfo.name.equals(
- intent.getComponent().getClassName())) {
- continue;
- }
- }
-
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
- if (resolveInfo != null) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- final String title = activityInfo.loadLabel(pm).toString();
- Drawable icon = activityInfo.loadIcon(pm);
-
- if (title != null && title.length() > 0 && icon != null) {
- final TextView tv = mIcons[index];
- tv.setText(title);
- icon = iconUtilities.createIconDrawable(icon);
- tv.setCompoundDrawables(null, icon, null, null);
- tv.setTag(intent);
- tv.setVisibility(View.VISIBLE);
- tv.setPressed(false);
- tv.clearFocus();
- ++index;
- }
- }
- }
-
-
- mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);
-
-
- for (; index < NUM_BUTTONS; ++index) {
- mIcons[index].setVisibility(View.GONE);
- }
- }
-
-
-
-
-
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
- if (! PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
- dismiss();
- }
- }
- }
- };
- }
至此,我们长按home事件的源代码就已经分析完毕。接下来,我们就模拟长按home事件,获取用户近期任务列表。相信有了上面的知识,下面的事情就不是难事了。
二:模拟长按home键弹出近期任务列表
有两个要注意的地方,这里先说出来。
a、final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE)中的ActivityManager.RECENT_IGNORE_UNAVAILABLE这个值不对我们开放,我们可以手动写死。它的值为:0x0002
b、注意加入权限。<uses-permission android:name="android.permission.GET_TASKS"/>
由于这个工程比较简单,我们只使用了一个MainActivity来实现功能。这里就大概的给童鞋们说一说。具体的可以看demo。
1、在应用程序启动的时候就去执行查找近期任务列表的操作。
- private Dialog mDialog;
- private static int MAX_RECENT_TASKS = 12;
- private static int repeatCount = 12;
-
- private List<HashMap<String,Object>> appInfos = new ArrayList<HashMap<String, Object>>();
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- reloadButtons();
- }
2、reloadButtons()方法的代码如下,获取近期任务代码与源码基本一致,只是在不显示launcher那里做了一些处理。按照下面的代码,如果我们不做处理,我们得到的近期任务列表数据将会少一个。具体看代码中的注释。
-
-
-
-
-
- private void reloadButtons() {
-
-
- final Context context = this;
- final PackageManager pm = context.getPackageManager();
- final ActivityManager am = (ActivityManager)
- context.getSystemService(Context.ACTIVITY_SERVICE);
-
-
-
- final List<ActivityManager.RecentTaskInfo> recentTasks =
- am.getRecentTasks(MAX_RECENT_TASKS + 1, 0x0002);
-
-
- ActivityInfo homeInfo =
- new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
- .resolveActivityInfo(pm, 0);
- int numTasks = recentTasks.size();
- for (int i = 0; i < numTasks && (i < MAX_RECENT_TASKS); i++) {
- HashMap<String, Object> singleAppInfo = new HashMap<String, Object>();
- final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
-
- Intent intent = new Intent(info.baseIntent);
- if (info.origActivity != null) {
- intent.setComponent(info.origActivity);
- }
-
- String currentInfo = "PackageName==" + intent.getComponent().getPackageName() + ",ClassName==" + intent.getComponent().getClassName();
-
-
-
- if (homeInfo != null) {
- if (homeInfo.packageName.equals(
- intent.getComponent().getPackageName())
- && homeInfo.name.equals(
- intent.getComponent().getClassName())) {
- MAX_RECENT_TASKS = MAX_RECENT_TASKS + 1;
- continue;
- }
- }
-
-
- intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- | Intent.FLAG_ACTIVITY_NEW_TASK);
-
- final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
- if (resolveInfo != null) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
- final String title = activityInfo.loadLabel(pm).toString();
- Drawable icon = activityInfo.loadIcon(pm);
-
- if (title != null && title.length() > 0 && icon != null) {
- singleAppInfo.put("title", title);
- singleAppInfo.put("icon", icon);
- singleAppInfo.put("tag", intent);
- }
- }
- appInfos.add(singleAppInfo);
- }
- MAX_RECENT_TASKS = repeatCount;
- }
3、通过上面的步骤,我们就获得了近期任务列表,并将其存放在了appInfos这个list中,接下来就是展示这个list的工作了。我这里就是简单的弹出一个dialog,dialog中放一个gridview。自定义gridview的adapter,在getView方法中进行任务点击事件的处理。代码如下:
MainActivity中的按钮点击事件:
- public void click(View view){
- int id = view.getId();
- switch (id) {
- case R.id.btn:
- generateDialog();
- break;
- }
- }
-
-
-
-
- public void generateDialog(){
- mDialog = new Dialog(this,R.style.dialog);
- LayoutInflater mInflater = LayoutInflater.from(this);
- View dialogView = mInflater.inflate(R.layout.choose_dialog, null);
- final GridView mGridView = (GridView) dialogView.findViewById(R.id.choose_dialog_gridview);
- mGridView.setAdapter(new MyAppAdapter());
- mDialog.setContentView(dialogView);
- mDialog.setCanceledOnTouchOutside(true);
- mDialog.show();
- }
弹出dialog中的gridview适配。
-
-
-
-
-
- private class MyAppAdapter implements ListAdapter{
-
- @Override
- public void registerDataSetObserver(DataSetObserver observer) {
- }
-
- @Override
- public void unregisterDataSetObserver(DataSetObserver observer) {
- }
-
- @Override
- public int getCount() {
- return appInfos.size();
- }
-
- @Override
- public Object getItem(int position) {
- return appInfos.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean hasStableIds() {
- return false;
- }
-
-
-
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- LayoutInflater mInflater = LayoutInflater.from(MainActivity.this);
- View infoView = mInflater.inflate(R.layout.choose_dialog_detail_info, null);
- ImageView mImageView = (ImageView) infoView.findViewById(R.id.app_icon);
- TextView mTextView = (TextView) infoView.findViewById(R.id.choose_dialog_detail_tv);
- String title = (String) appInfos.get(position).get("title");
- Drawable icon = (Drawable) appInfos.get(position).get("icon");
- Intent singleIntent = (Intent) appInfos.get(position).get("tag");
- infoView.setTag(singleIntent);
- mImageView.setImageDrawable(icon);
- mTextView.setText(title);
- infoView.setOnClickListener(new SingleAppClickListener());
- return infoView;
- }
-
- @Override
- public int getItemViewType(int position) {
- return 0;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public boolean isEmpty() {
- return false;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
- }
图标点击事件处理:
-
-
-
-
-
- private class SingleAppClickListener implements View.OnClickListener{
- @Override
- public void onClick(View v) {
- Intent intent = (Intent)v.getTag();
- if (intent != null) {
- intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
- try {
- MainActivity.this.startActivity(intent);
- if(mDialog != null)
- mDialog.dismiss();
- } catch (ActivityNotFoundException e) {
- Log.w("Recent", "Unable to launch recent task", e);
- }
- }
- }
-
- }
用到的一些资源文件和布局文件如下:
choose_dialog.xml 对话框。
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- >
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center"
- >
- <GridView
- android:id="@+id/choose_dialog_gridview"
- android:layout_width="wrap_content"
- android:gravity="center"
- android:layout_height="wrap_content"
- android:verticalSpacing="15dp"
- android:horizontalSpacing="15dp"
- android:numColumns="3"
- android:background="@drawable/bg_y"
- ></GridView>
- </LinearLayout>
-
-
- </LinearLayout>
choose_dialog_detail_info.xml gridview中每一个item的布局。
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center"
- >
-
- <ImageView
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:src="@drawable/ic_launcher"
- android:layout_centerInParent="true"
- android:id="@+id/app_icon"
- />
-
- <TextView
- android:layout_marginTop="20dp"
- android:layout_below="@+id/app_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:background="@null"
- android:text="cccccccccc"
- android:layout_centerHorizontal="true"
- android:id="@+id/choose_dialog_detail_tv"
- />
-
- </RelativeLayout>
colors.xml
- <?xml version="1.0" encoding="utf-8"?>
- <!-- Copyright (C) 2007 The Android Open Source Project -->
-
- <resources>
- <color name="transparent">#00000000</color>
- </resources>
styles.xml
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <style name="dialog" parent="@android:style/Theme.Dialog">
- <item name="android:windowFrame">@null</item><!--边框-->
- <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
- <item name="android:windowIsTranslucent">true</item><!--半透明-->
- <item name="android:windowNoTitle">true</item><!--无标题-->
- <item name="android:windowBackground">@color/transparent</item><!--背景透明 -->
- <item name="android:backgroundDimEnabled">true</item><!--背景暗淡-->
- </style>
- </resources>
到这里,我们模拟长按home的代码就全部展示完了。有不足的地方望指正,谢谢。