车载Android导航系统framework层面上的系统修改问题汇总

开篇语

    代码太多,bug太猛  ----米德

1, 关于Launcher的修改方案(基于api19源码 android4.4)

控制桌面图标显示(Launcher)
LauncherModel.java (loadAllAppsByBatch()方法中)

Bitmap createIconBitmap(Drawable icon, Context context) //创建图标函数

loadAllAppsByBatch //加载所有图标,过滤

//加载预设图标
loadDefaultFavoritesIfNecessary
private int loadFavorites(SQLiteDatabase db, int workspaceResourceId)

loadWorkspace()

//从菜单回主页
private void hideAppsCustomizeHelper(
mSearchDropTargetBar

mSearchDropTargetBar.setVisibility(View.INVISIBLE);

MotionEvent.ACTION_UP

    	if(Launcher.pThis.mSearchDropTargetBar != null)
    	{
    		Launcher.pThis.mSearchDropTargetBar.setVisibility(View.VISIBLE);		
    	}

loadLabel(mPackageManager)

//文字底色

public void draw(Canvas canvas) {}

//图标大小
主页
value-land/dimens.xml
<dimen name="app_icon_size">134px</dimen>
<dimen name="workspace_cell_width_land">90dp</dimen>
<dimen name="workspace_cell_height_land">82dp</dimen>

/********************************AppsCustomize*******************************/
//AppsCustomize(分Land和port)
	/***共用部分***/
	布局位置:apps_customize_pane.xml(可设置是否显示上部的应用图标按钮和小部件、应用选项)	
	
	values/styles.xml
	<style name="WorkspaceIcon.Landscape.AppsCustomize">
        	<item name="android:background">@null</item>//app背景
        	<item name="android:paddingTop">0dp</item>  
        	<item name="android:paddingBottom">3dp</item>
        	<item name="android:drawablePadding">-5dp</item>//图片和文字的间距(值为负数时,间隔会变大,反之变小)
    	</style>
	/***非共用部分***/
	Land : values-land/dimens.xml
	Land : values-port/dimens.xml
	//APPS 图标大小
	public class AppsCustomizePagedView{
		mAppIconSize = resources.getDimensionPixelSize(R.dimen.app_icon_size) * 3/4;
	}
	
	//APPS  格子大小(高宽)values-land/dimens
	<dimen name="apps_customize_cell_width">86dp</dimen>
	<dimen name="apps_customize_cell_height">80dp</dimen>
		
	
	//图标间隔(apps)
		launcher:pageLayoutHeightGap="40dp"
		launcher:pageLayoutWidthGap="45dp"
		
		<dimen name="apps_customize_pageLayoutHeightGap">-10dp</dimen>//水平间隙
		<dimen name="apps_customize_pageLayoutWidthGap">-1dp</dimen>//竖直间隙
	
	//一页中,行列widget个数
    	<integer name="apps_customize_widget_cell_count_x">3</integer>
    	<integer name="apps_customize_widget_cell_count_y">3</integer>

	//改变图片大小
	Utilites.java中的createIconBitmap2();
	int sourceWidth = 65;// icon.getIntrinsicWidth();
	int sourceHeight = 65;// icon.getIntrinsicHeight();
	*****注:注意区分createIconBitmap2()和createIconBitmap()的不同点
				createIconBitmap2()控制AppsCustomize中每个app的外形
				createIconBitmap()控制工作空间中每个app的外形(workplace)
	
/***************************end**************************************/


//文字大小(workplace和AppsCustomize中的所有app中文字大小)
	公用部分:
 	<dimen name="workspace_icon_text_size">14sp</dimen>

//文字座标
 styles.xml 
//主页
    <style name="WorkspaceIcon.Landscape">
        <item name="android:drawablePadding">@dimen/app_icon_drawable_padding_land</item> //调这句可以调节文字和图片的相对距离
        <item name="android:paddingLeft">4dp</item>
        <item name="android:paddingRight">4dp</item>
        <item name="android:paddingTop">@dimen/app_icon_padding_top</item>
        <item name="android:paddingBottom">4dp</item>
    </style>
//apps
    <style name="WorkspaceIcon.Landscape.AppsCustomize">
           <item name="android:drawablePadding">-22dp</item>
//加载图标到某一页
addViewToCellLayout

/*****************************************folder**********************************/
//文件夹大小(未打开)
	1,values-land/dimens.xml 
	  <dimen name="folder_preview_size">110dp</dimen>
	2,folder_icon.xml(land/port)
	  修改文件夹包裹大小,以及folderName相关属性和包裹图片
	3,values/dimens.xml    folder_name_padding值 和 folder_preview_padding值
	  修改文件夹下文件名与上部图片的间距

values/dimens.xml

//打开文件夹后大小(每个图标大小)
    user_folder.xml
    <dimen name="folder_cell_width">100dp</dimen>
    <dimen name="folder_cell_height">100dp</dimen>

//打开文件夹后的位置 
	private void centerAboutIcon() {
	  ..................................
		lp.width = width;
		lp.height = height;
		lp.x = left;
		lp.y = top - 100;
		}

/***************************************************end***************************************/

//单独改变 APPS 图标大小

    public PagedViewCellLayout(Context context, AttributeSet attrs, int defStyle) {

	......................................................        

        mChildren.setScaleX(0.8f);
        mChildren.setScaleY(0.8f);
        addView(mChildren);
    }
		
}

/***************************************************Widget***************************************/
//关于桌面小插件(Widget)的问题
1 . AppsCustomizePagedView.java中的onPackagesUpdated()方法; boolean bShowWidget = true;//控制是否显示

2 . apps_customize_widget.xml每个widget的layout

3 . apps_customize_pane.xml控制Widget界面中Widget的X/Y方向上的个数
   		launcher:widgetCellHeightGap="35dp"
                launcher:widgetCellWidthGap="40dp"
                launcher:widgetCountX="5"
                launcher:widgetCountY="2" 

/***************************************************end***************************************/



/******************加载进度圈***********************/
	apps_customize_progressbar.xml

/********************桌面图标拖拽******************/
Launcher.java (onLongClick)--->Workspace.java(beginDragShared())--->DragControll.java (startDrag()-->结束拖动endDrag())

/********************************SystemUi的高度******************************/
	private int getStatusBarHeight() {
		if (statusBarHeight == 0) {
			try {
				Class<?> c = Class.forName("com.android.internal.R$dimen");
				Object o = c.newInstance();
				Field field = c.getField("status_bar_height");
				int x = (Integer) field.get(o);
				statusBarHeight = getResources().getDimensionPixelSize(x);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return statusBarHeight;
	}
/****************************************end**************************/



/********************************对于工作空间的拖拽问题**************************************/
	1, 进入Launcher.java中的onLongClick()方法,如果是小图标(mWorkspace.startDrag(longClickCellInfo))。
		如果是空的区域(startWallpaper()),开始壁纸设置(调用系统壁纸设置方法)。itemUnderLongClick == null成立。
	2, if(itemUnderLongClick != null),长按的小图标,调用Workspace.java中的startDrag()--->beginDragShared(View child, 		   	DragSource source)--->调用DragController.java中的第二个startDrag()方法<第一个startDrag()方法,会在页面滑		 	动时调用>,可以在该方法中控制删除栏或者搜索栏的显示和隐藏。
		注:Launcher.pThis.mDragLayer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
		Launcher.pThis.mDragLayer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);(SystemUI的显示和隐藏)
		其他Launcher中的视图可以通过setTop(),和setY()来控制。setY()相对于父视图(可以先Top在Y)
	3, 拖拽结束时会调用DragController.java中的endDrag(),控制删除栏或者搜索栏的显示和隐藏。

/*********************************************end********************************************/

/**************************Launcher 中所有app列表界面动态壁纸问题****************************/
1 .apps_customize_pane.xml  android:background="#00000000"> // 原值为#FF000000(黑色背景)
2 .请修改packages\apps\Launcher2\src\com\android\launcher2\Launcher.java的hideAppsCustomizeHelper(boolean animated, final boolean springLoaded)方法,如下:
	......
	setPivotsForZoom(fromView, scaleFactor);
	updateWallpaperVisibility(true);
	showHotseat(animated);
	if(mDockDivider != null) { // add
		mDockDivider.setVisibility(View.VISIBLE); // add
   	} // add

3 .请修改Launcher.java的showAppsCustomizeHelper(boolean animated, final boolean springLoaded) 方法		,如下:
	......
	// Shrink workspaces away if going to AppsCustomize from workspace 
	mWorkspace.changeState(Workspace.State.SMALL, animated); 
	// and hide hotseat and dock divider
	hideHotseat(false); // add
	if(mDockDivider != null) {// add
		mDockDivider.setVisibility(View.INVISIBLE); // add
	}// add
	if(mWorkspace != null) {// add
		mWorkspace.setVisibility(View.INVISIBLE);// add
    	}// add

4 .请修改Launcher.java 文件,将showAppsCustomizeHelper(boolean animated, final boolean springLoaded)方法中出现的两处updateWallpaperVisibility(false);注释掉
	第一处是在此函数内的 public void onAnimationEnd(Animator animation) 方法的最后
	第二处是在showAppsCustomizeHelpe方法的最后

5 .请修改Launcher.java的enterSpringLoadedDragMode()方法,如下:
	void enterSpringLoadedDragMode() {
		if (mState == State.APPS_CUSTOMIZE) {
			mWorkspace.setVisibility(View.VISIBLE); // add
			mWorkspace.changeState(Workspace.State.SPRING_LOADED);
			hideAppsCustomizeHelper(true, true);
			hideDockDivider();
			mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
		}
    	}

	ICS/ICS2 版本按照以上修改即可,如果是JB版本需要在以上修改的基础上加入如下修改:

6、请修改Launcher.java 中disableWallpaperIfInAllApps() 方法,如下:
	void disableWallpaperIfInAllApps() {
	// Only disable it if we are in all apps
		if (isAllAppsVisible()) {
			if (mAppsCustomizeTabHost != null &&
				!mAppsCustomizeTabHost.isTransitioning()) {
				updateWallpaperVisibility(true); // modify 
			}
		}
	}

7、请修改packages\apps\Launcher2\res\layout\apps_customize_pane.xml中id 为 animation_buffer 的这个控件, 将其 android:background 设置为 #0000000
	如果JB2/JB3 的版本还需要在以上修改的基础上加入如下修改:

8、请修改Launcher.java的onResume()方法:
	将setWorkspaceBackground(mState == State.WORKSPACE);  修改为setWorkspaceBackground(true);
	修改了第7步之后, 在切换 widget 及 app 的时候, 可能会短时间的看到 widget 与 app 相叠加的画面。
	这是 appsCustomizeTabHost.java 的 onTabChanged 方法内的 animSet.playTogether(outAnim, inAnim); 所指定的效果,可以自行修改。

/*********************************************end********************************************/


//改变所有app上tap字体颜色大小(launcher.AccessibleTabView.java)
  显示隐藏Tap(AppsCustomizeTabHost.xml)

//控制工作空间中是否接受快捷方式(拖动app是否能够添加在该页面,通过screen控制) Workplace.java  中的onDrop();

//systemUI 快捷开关
//PhoneStatusBar

    // ensure quick settings is disabled until the current user makes it through the setup wizard
    private boolean mUserSetup = true;

    mUserSetup = userSetup  = true;

增加,减少 tile
//QuickSettings.java
private void addUserTiles(ViewGroup parent, LayoutInflater inflater)


//显示3G信号
    <!-- Show phone (voice) signal strength instead of data in mobile RSSI. -->
    <bool name="config_showPhoneRSSIForData">true</bool>
//policy/NetworkController.java
public NetworkController(Context context)
{
	mShowPhoneRSSIForData = true;
	mShowAtLeastThreeGees = true;
	mAlwaysShowCdmaRssi = true;
}


//设置某个cellLaoyut不可用(添加shortcut)
installShortcutReceiver.java     processInstallShortcut(Context context)   final int screen = Launcher.DEFAULT_SCREEN;

调整statusbar的高度,frameworks/base/core/res/res/values/dimens.xml 中,修改 <dimen name="status_bar_height">25dip</dimen> 的值


/*************************拖动删除app**********************/

只要拖动到上面区域就会有相应的响应

1,DeleteDropTarget.java 中onDragStart(DragSource source, Object info, int dragAction)方法中
	调整setCompoundDrawablesWithIntrinsicBounds(null, mUninstallDrawable, null, null);
							(左,上,右,下)

2,在drop_target_bar.xml中(有两处更改)
 	将android:drawableLeft="@drawable/info_target_selector" 更换成	android:drawableTop="@drawable/info_target_selector";

3,在values-land/styles.xml 和values/styles.xml中,在DropTargetButton样式中,将
 	<item name="android:layout_width">wrap_content</item>更换
 	<item name="android:layout_width">match_parent</item>

/********外加说明************/
1..修改qsb_bar.xml和drop_target_bar.xml中的style样式,会改变删除条的布局位置以及删除图标等


说明:只要进行了第三步操作,就会只要拖动就会有响应,反之,只会拖动到相应的图标时才会有响应
      拖动后会进入DragControll.java中的第二个startDrag();
      if(Launcher.pThis.mSearchDropTargetBar != null)
    	{
    		flag = true;
    		Launcher.pThis.mSearchDropTargetBar.setVisibility(View.GONE);	
    	}  
	/****控制显示/隐藏


1 . setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)

2 .setCompoundDrawables(left, top, right, bottom); 


3 .	setCompoundDrawablesRelative(start, top, end, bottom);  

三者的区别

/**********************************end*********************************

2, 关于Dialog样式问题

	透明背景(加入样式)
    <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">false</item>
        <!-- 半透明 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 无标题 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <!-- 背景透明 -->
        <item name="android:backgroundDimEnabled">false</item>
        <!-- 模糊 -->
    </style>

3,关于开机默认时间12_24设置 

/**********************1步**********************/
	/home/*****/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java中的loadSystemSettings(SQLiteDatabase db) 
	加入loadStringSetting(stmt, Settings.System.TIME_12_24, R.string.time_12_24);

/**********************2步**********************/	 
	/home/*****/frameworks/base/packages/SettingsProvider/res/values/defaults.xml中  
	加入<string name="time_12_24" translatable="false">24</string>

/**********************编译**********************/
	直接编译外部framework中 mmm ./frameworks/base/packages/SettingsProvider

/**********************说明**********************/
	如果没有1步和2步,系统升级的时候默认是12小时制的时间显示

4,关于系统时间和系统语言问题

1,获取问题
日/月/年    时:分
dd/MM/yy    hh:mm      当hh12小时制   HH24小时制
Date date = new Date(time);
SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd  HH:mm");
fileTime = format.format(date);
1,System.currentTimeMillis() 获取机器当前时间long
2,SystemClock.uptimeMillis() 获取机器boot完以后到当前的时间(更新开机时间) 系统时间变化以后,会有影响。
//2,设置系统时间
	
package com.md.carinfo.util;

import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import android.os.SystemClock;

public class SystemDateTime {
    
   static final String TAG = "SystemDateTime"; 
    
   public static void setDateTime(int year, int month, int day, int hour, int minute,int second) throws IOException, InterruptedException {
       requestPermission();
       Calendar c = Calendar.getInstance();
       c.set(Calendar.YEAR, year);
       c.set(Calendar.MONTH, month-1);
       c.set(Calendar.DAY_OF_MONTH, day);
       c.set(Calendar.HOUR_OF_DAY, hour);
       c.set(Calendar.MINUTE, minute);
       c.set(Calendar.SECOND, minute);
       long when = c.getTimeInMillis();

       if (when / 1000 < Integer.MAX_VALUE) {
           SystemClock.setCurrentTimeMillis(when);
       }

       long now = Calendar.getInstance().getTimeInMillis();
       //Log.d(TAG, "set tm="+when + ", now tm="+now);

       if(now - when > 1000)
           throw new IOException("failed to set Date."); 
        
   }

   public static void setDate(int year, int month, int day) throws IOException, InterruptedException {

       requestPermission();

       Calendar c = Calendar.getInstance();

       c.set(Calendar.YEAR, year);
       c.set(Calendar.MONTH, month);
       c.set(Calendar.DAY_OF_MONTH, day);
       long when = c.getTimeInMillis();

       if (when / 1000 < Integer.MAX_VALUE) {
           SystemClock.setCurrentTimeMillis(when);
       }

       long now = Calendar.getInstance().getTimeInMillis();
       //Log.d(TAG, "set tm="+when + ", now tm="+now);

       if(now - when > 1000)
           throw new IOException("failed to set Date.");
   }

   public static void setTime(int hour, int minute) throws IOException, InterruptedException {
        
       requestPermission();

       Calendar c = Calendar.getInstance();

       c.set(Calendar.HOUR_OF_DAY, hour);
       c.set(Calendar.MINUTE, minute);
       long when = c.getTimeInMillis();

       if (when / 1000 < Integer.MAX_VALUE) {
           SystemClock.setCurrentTimeMillis(when);
       }

       long now = Calendar.getInstance().getTimeInMillis();
       //Log.d(TAG, "set tm="+when + ", now tm="+now);

       if(now - when > 1000)
           throw new IOException("failed to set Time.");
   }
    
   static void requestPermission() throws InterruptedException, IOException {
       createSuProcess("chmod 666 /dev/alarm").waitFor();//获取系统时间设置权限,记得在AndroidManifest.xml中加入相应的权限,如果系统大于6.0 还要添加动态权限
   }
    
   static Process createSuProcess() throws IOException  {
       File rootUser = new File("/system/xbin/ru");
       if(rootUser.exists()) {
           return Runtime.getRuntime().exec(rootUser.getAbsolutePath());
       } else {
           return Runtime.getRuntime().exec("su");
       }
   }
    
   static Process createSuProcess(String cmd) throws IOException {

       DataOutputStream os = null;
       Process process = createSuProcess();

       try {
           os = new DataOutputStream(process.getOutputStream());
           os.writeBytes(cmd + "\n");
           os.writeBytes("exit $?\n");
       } finally {
           if(os != null) {
               try {
                   os.close();
               } catch (IOException e) {
               }
           }
       }
       return process;
   }
}
获取系统时区/国家
String str1 = Locale.getDefault().getLanguage();
String str2 = Locale.getDefault().getCountry();
return "/" + str1 + "_" + str2;
例:en_US/zh_CH

5,关于开机动画与开机logo的相关问题 

1  .开机动画
	1> 资源路径:out/.../system/media
	desc.txt中的1024 600 12,1024,600表示分辨率,12表示每秒多少帧动画
	
	关于动画的制作
	只能压缩成.zip格式的文件,选择无压缩比的方式压缩(压缩方式选存储),生成bootanimation.zip

2  .开机logo
	1> 资源路径:相应的系统平台目录下/platform/uboot/tools/logo.mrf
	关于logo文件的制作


/**********************************注意事项*********************************/

如果只跑一遍的话,part0中80-90张,无限循环的话,没有什么要求(最好在15-20张左右)
1024 600 12    (一般控制在12-25张每帧)
p 1 0 part0    1 代表只跑一遍(0 无限循环)
p 0 0 part1    


	

6,关于IBinder和启动系统服务的问题

/**********************************************1*********************************************/
首先调用SystemServer.java的文件(framework/base/services/java)
通过run();启动相应的*Service的java文件类,
*Service.java继承相应的aidl文件的Stub,实现aidl文件中的所有方法,
在core代码中(生成jar包的sdk中)定义一个Manger类,和上面一步所需的aidl文件,
在Manger类中,通过IBinder的机制实例化*Service.aidl文件,获得相应的*Service.aidl实例类。
实例化代码:   	public TestManger() {

		if(mTestService == null){
			IBinder b = ServiceManager.getService("md");
	        		mTestService = ITestService.Stub.asInterface(b);
	        		if(mTestService != null)
			Log.i("md", "========TestManger is ready==========");
		}
    	}
通过实例化的*Service.aidl去调用底层相应*Service.java中的方法。(通过Stub机制)

重点:IBinder/Binder
1,通过aidl文件为外部提供相应的方法的代码机制,及实现逻辑(Stub)。
2,IBinder机制的实现
3,注意在Manger类中调用底层方法时注意try()catch()机制,如果内部*Service.java文件抛出了异常。就必须得try,不然编译不通过。


/**********************************************2*********************************************/
IBinder/Binder机制

实例化

/******************获取Service*******************/
1   .通过构造函数获取(实例化)
	public McuComManager() {

		if(mMcuComService == null)
		{
			IBinder b = ServiceManager.getService(Context.MCUCOM_SERVICE);
	        mMcuComService = IMcuComService.Stub.asInterface(b);
	        if(mMcuComService != null)
			Log.i(TAG, "McuComManager is ready");//获取成功
		}
    }

7,简单的旋转动画设置问题

***********************************旋转动画**********************************/
	RotateAnimation animation2 = new RotateAnimation(0, 359, pivotXType, pivotXValue, pivotYType, pivotYValue)
	LinearInterpolator lin = new LinearInterpolator();  //匀速
	AccelerateInterpolator i = new AccelerateInterpolator(); //加速
	DecelerateInterpolator d = new DecelerateInterpolator(); //减速
	animation.setInterpolator(i);

	animation.setDuration(1000);
	animation.setRepeatCount(-1); //往返循环(如果设置的值大于-1时,设置多少就旋转多少)
	旋转中心
	RotateAnimation.RELATIVE_TO_SELF  //相对于自己来说
	RotateAnimation.RELATIVE_TO_PARENT  //相对于父空控件来说
		
	对于视图本身为旋转中心
	RotateAnimation animation = new RotateAnimation(0, 360,RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);

			  
	

8,壁纸问题

/*********************设置壁纸问题***************************/
	Intent chooseIntent = new Intent(Intent.ACTION_SET_WALLPAPER);  
        //启动系统选择应用  
	Intent intent = new Intent(Intent.ACTION_CHOOSER);  
        intent.putExtra(Intent.EXTRA_INTENT, chooseIntent);  
        intent.putExtra(Intent.EXTRA_TITLE, "选择壁纸");  
    	startActivity(intent);  

 	/** 
 	 *  
  	 * 当别的应用程序改变了壁纸后,这里定义一个BroadcastReceiver来接受通知 
  	 * 
  	 */  
  	class WallpaperIntentReceiver extends BroadcastReceiver{  
      
    	private Application application;  
    	//WeakReference使得WallpaperIntentReceiver不会因为Launcher的引用而被推迟注销掉  
    	private WeakReference<UorderLauncher> rLauncher;  
  
    	public WallpaperIntentReceiver(Application application, UorderLauncher launcher) {  
       	  this.application = application;  
      	  this.rLauncher = new WeakReference<UorderLauncher>(launcher);  
	}  

/*********************开机默认壁纸问题***************************/
	1,默认壁纸修改
	/framework/base/core/res/res/values/config.xml
	编译路径:mmm ./framework/base/core/res/res
	<string name="default_wallpaper_component">@null</string> ---静态  默认launcher第一张图片(framework/res中没有的话)
	<string name="default_wallpaper_component" able="false">******/.WallpaperService</string> --动态

9,不同语言的布局和时间显示问题(阿拉伯数字显示)的方法

/********************************布局问题**************************
当系统设为阿拉伯文整体文字都会从右到左,但有些是不要的,要从左到右:apps\Launcher2\res\layout-land/launcher.xml 23行增加:  android:layoutDirection="ltr" 即可
或者在每个应用中得xml文件根布局中加入android:layoutDirection="ltr"   (当前显示什么样,设置后还是显示当前这个样子)

/********************************时间显示问题**************************
      Locale locale = new Locale("America/Los_Angeles"); ===数字和时钟等都不用反。设为本地码Los_Angeles
      DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
      Format = new DecimalFormat(".00",symbols);   

      系统标题栏更改时间的显示:
      /*****/frameworks/base/packages/SystemUI
      /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

      //阿拉伯文显示,调用本地码函数。 PhoneStatusBar.java
      Locale locale1 = new Locale("America/Los_Angeles");
      SimpleDateFormat dateFormat24 = new SimpleDateFormat(" HH:mm", locale1);
      SimpleDateFormat dateFormat12 = new SimpleDateFormat(" hh:mm", locale1);
      Long long1 = System.currentTimeMillis();
      if(b24) time = dateFormat24.format(long1);
      else time = dateFormat12.format(long1);

10,byte数组转换成中文

byte[] data = {0x3b,0x5d};//16进制
EncodingUtils.getString(data, "gb2312");  //后面参数可更改

11,关于默认启动Launcher的设置(多个Launcher共存时,不用弹框提示<貌似Android6.0第一次升级起来后不行,以后都可以>)

/********************默认Launcher的设置********************/

1,在ActivityMangerService.java中找到startHomeActivityLocked(int userId)方法	

2,在该方法中,代码开头加入自定义的方法	setDefaultLauncher();

//md add 2016/06/23  setDefultLauncher
	
	int launcherType = 1;
	private void getLauncherType(){
		try {
			Context otherAppsContext = mContext.createPackageContext("com.android.settings", Context.CONTEXT_IGNORE_SECURITY);
			SharedPreferences ttSharedPreference = otherAppsContext .getSharedPreferences("MainStyle",Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
			launcherType = ttSharedPreference.getInt("pos", 1);
		//Log.i("md","============launcherType= "+launcherType);	 
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}

	}

	private void setLauncherIntent(String packageName ,String className ){
		IPackageManager pm = ActivityThread.getPackageManager();  
			//清除当前默认launcher 
        			ArrayList<IntentFilter> intentList = new ArrayList<IntentFilter>();  
        			ArrayList<ComponentName> cnList = new ArrayList<ComponentName>();  
       	 		mContext.getPackageManager().getPreferredActivities(intentList, cnList, null);  
        			IntentFilter dhIF = null;  
        			for(int i = 0; i < cnList.size(); i++) {  
            			dhIF = intentList.get(i);  
           			if(dhIF.hasAction(Intent.ACTION_MAIN) && dhIF.hasCategory(Intent.CATEGORY_HOME)) {  
                				mContext.getPackageManager().clearPackagePreferredActivities(cnList.get(i).getPackageName());  
            			}  
			}
			
			//获取所有launcher activity 
        			Intent intent = new Intent(Intent.ACTION_MAIN);  
        			intent.addCategory(Intent.CATEGORY_HOME);  
        			List<ResolveInfo> list = new ArrayList<ResolveInfo>();  
					final int callingUserId = UserHandle.getCallingUserId();
        			try {  
            			list = pm.queryIntentActivities(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), PackageManager.MATCH_DEFAULT_ONLY,callingUserId);  
        			}catch (RemoteException e) {  
            			throw new RuntimeException("Package manager has died", e);  
        			}
        			// get all components and the best match  
        			IntentFilter filter = new IntentFilter();  
        			filter.addAction(Intent.ACTION_MAIN);  
        			filter.addCategory(Intent.CATEGORY_HOME);  
        			filter.addCategory(Intent.CATEGORY_DEFAULT);
        			final int N = list.size();   
			//设置默认launcher 
        			ComponentName launcher = new ComponentName(packageName, className);  
					ComponentName[] set = new ComponentName[N];  
			
					int defaultMatch = 0;  
        			for (int i = 0; i < N; i++) {  
            			ResolveInfo r = list.get(i);  
            			set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name);  
            			if(launcher.getClassName().equals(r.activityInfo.name)) {
                				defaultMatch = r.match;
            			}
        			}
			//将设置的默认launcher,添加到系统偏好                
        			try {  
            			pm.addPreferredActivity(filter, defaultMatch, set, launcher,2012121);  
        			} catch (RemoteException e) {  
            			throw new RuntimeException("factorytest.MainActivity : Package manager has died", e);  
       		 	}    
	}
	
     	private void setDefaultLauncher(){
		getLauncherType();
		Log.i("md","======setDefaultLauncher==="+launcherType);
		//以下主要是为了多个Launcher存在是设置那个Launcher为默认()
		if(launcherType==1){
			String packageName = "com.XX.launcher";
        	String className = "com.XX.XXXX.Launcher"; 
			setLauncherIntent(packageName ,className );
		}else if(launcherType==2){
			String packageName = "com.XX.launcher";
        	String className = "com.XX.XXXX.Launcher"; 
			setLauncherIntent(packageName ,className );
		}else if( .... )
	}

//end

12,破解手机读写权限(有的手机不行,实测oppo可以)

1  .
	# su 
	# mount -o rw,remount -t yaffs2 /dev/block/mtdblock3 /system //参数可能有的手机不同(换成mount -o rw,remount /system)试下
	# chmod 777 /system 
	# cd system
	# chmod 777 app
	# cd app
	# chmod 777 ***.apk
	# exit

2  .
	adb shell
	#su
	#mount -o rw,remount /system  (给system目录赋予读写权限)
	
/************************注意开两个cmd************************/

13,关于系统编译问题

服务(系统)
/home/../../**/frameworks/base/services/java/com/android/server  (代码位置)

/home/../../**/frameworks/base/services/java   (编译位置)

/home/../../**/out/target/product/ac8317/system/framework  (生成jar包位置)
/**********************************************************************************************************************/

导航栏(系统)
/home/../../**/frameworks/base/packages/SystemUI/ status_bar.XML

/home/../../**/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.JAVA

/home/../../**/out/target/product/ac8317/system/app
导航栏高度设置(直接在framework中) systemUI中只会改变所占状态栏的高度,不是改变实际的状态栏高度
(获得状态栏的高度)
Resources.getSystem().getDimensionPixelSize(Resources.getSystem().getIdentifier("status_bar_height","dimen", "android"));

/**********************************************************************************************************************/
设置(系统)
./selfbuild cpoverlay

/home/../../**/packages/apps/Settings//编译路径

/home/../../**/device/atc/ac8317/.overlay/packages/apps/Settings//图片路径

/home/../../**/packages/apps/ATCSetting  (app)(本地)

/**********************************************************************************************************************/
音量条(系统)
./selfbuild cpoverlay
/home/../../**/frameworks/base/core/res/res/layout/volume_adjust.xml   (layout位置)

/home/../../**/frameworks/base/core/res/res/layout/volume_adjust_item.xml

/home/../../**/frameworks/base/core/java/android/view/VolumePanel.java   (代码位置)

/home/../../**/device/atc/ac8317/.overlay/frameworks/base/core/java/android/view/VolumePanel.java

//以下是音量条Window中音量条样式,(颜色/按键效果)
scrubber_primary_holo.9.png  /   scrubber_secondary_holo.9.png   /    scrubber_control_pressed_holo.png  /   scrubber_control_normal_holo.png

编译:
 #:source ./selfenv
 #:./selfbuild cpoverlay
 #: mmm ./frameworks/base/core/res
 #: mmm ./frameworks/base/
 #: ./selfbuild makeimage

./selfbuild makeimage  //打包

/**********************************************************************************************************************/
wifi调试代码  (要wifi调试app的我的百度云盘中有)
	su        
	setprop service.adb.tcp.port 5555  
	stop adbd     
	start adbd   //还可以直接用adb tcpip 5555 ... adb connect 192.168.1.111
/**********************************************************************************************************************/


14,关于Android Studio的问题

注意,只要时修改了build.gradle中的文件,都需要重新编译工程,那个module报错,那就修改那个module下的build.gradle文件,例如问题5

********* 1 *****************编译版本不对buildToolsVersion或者过小

修改Android studio中的build.gradle文件(基于app)

********* 2 *****************Android studio中minsdk>devicesdk的处理

修改Android中build.gradle文件(基于app)   minSdkVersion xx


********* 3 *****************Error running NovaCam: This version of Android Studio is incompatible with the Gradle Plugin used. Try disabling Instant Run 				(or updating either the IDE or the Gradle plugin to the latest version)

修改整个工程中的build.gradle文件(基于整个工程)  
	dependencies {
        	classpath 'com.android.tools.build:gradle:2.3.3'
    	}

如果时Android Studio2.0  修改为 classpath 'com.android.tools.build:gradle:2.0.0' 重新编译


******** 4 **********Cause: error in opening zip file
		Consult IDE log for more details (Help | Show Log) (183ms)

修改gradle-wrapper.properties中的distributionUrl
	Android studio3.0的为 https\://services.gradle.org/distributions/gradle-4.1-all.zip

******** 5 ***********Error:Execution failed for task ':TerminalLibrary:transformResourcesWithMergeJavaResForDebug'.
		> More than one file was found with OS independent path 'META-INF/LICENSE'

在报该编译错误的module的build.gradle中加入如下配置项,排除掉中间生成的META-INF/xxx文件
android{
packagingOptions {
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/LICENSE.txt'
    exclude 'META-INF/NOTICE.txt'
}
}


******** 6 **********关于动态引入的库中文件找不到的错误 Caused by: java.lang.ClassNotFoundException: Didn't find class
	修改setting.gradle中引入的库名称(删掉)
	重新编译工程,会编译不通过 build.gradle中dependencies下compile project(****)找不到库
	修改setting.gradle中,加入刚删除的库名称
	重新编译工程,就ok了      可能缓存弄的


******** 7 *****************************
	Warning:The `android.dexOptions.incremental` property is deprecated and it has no effect on the build process.
	build.gralde中做了解决操作,其中有个如下的配置  incremental true

    	dexOptions {
        	dexInProcess true
        	preDexLibraries true
        	javaMaxHeapSize "8g"
        	incremental true   直接删除即可
    	}

	这个功能在studio之前的版本中是默认关闭的,现在android studio在不断优化,更新之后貌似不需要再特意配置了,直接删除即可



10下   #*32279    95272046

15,关于keystore

1 ,查看keystore    命令 keytool -list -keystore debug.keystore   (目录c:\user\admin\.android)

	默认的keystore只要一升新的操作系统,就会随之而变,自己生成的则不会(有效期内)

	默认的keystore路径:C:\Users\Administrator\.android
        84:6D:0A:D2:6B:D3:D9:2C:39:9F:68:55:68:E8:9E:39:DF:11:7B:28 (本机的keystore)

16,关于socket连接超时问题

1....   这个方法得到的对象会有超时异常  (下面的异常都会执行)
	socket = new Socket();
	InetAddress address = InetAddress.getByName("192.168.1.254");	ip地址
	InetSocketAddress socketAddress = new InetSocketAddress(address , 3333);  端口号
	socket.connect(socketAddress, 5000);  连接超时时间
	socket.setSoTimeout(4000);   response超时时间


2....	这个方法得到的对象没有超时现象(一直在那里等待连接),也不是所有的都是那样
	Android4.4.2的会阻塞超时异常,Android6.0的不会阻塞超时异常
	
	socket = new Socket("192.168.1.254",3333);

        公共异常
	try {
	
	} catch (IllegalArgumentException e1) {
		Logs.v("socket service - SocketReceiveThread::IllegalArgumentException!");
	} catch (UnknownHostException e1) {
		Logs.v("socket service - SocketReceiveThread::UnknownHostException!");	
	} catch (IOException e1) {
		Logs.v("socket service - SocketReceiveThread::IOException!");	
	}

17,获取网络ip地址

public static String getIPAddress(Context context) {
        NetworkInfo info = ((ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
                try {
                    //Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                                return inetAddress.getHostAddress();
                            }
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                }

            } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
                WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4地址
                return ipAddress;
            }
        } else {
            //当前无网络连接,请在设置中打开网络
        }
        return null;
    }

    /**
     * 将得到的int类型的IP转换为String类型
     *
     * @param ip
     * @return
     */
    public static String intIP2StringIP(int ip) {
        return (ip & 0xFF) + "." +
                ((ip >> 8) & 0xFF) + "." +
                ((ip >> 16) & 0xFF) + "." +
                (ip >> 24 & 0xFF);
    }

18,关于raw的用法

android.resource://" + getPackageName() + "/"+ R.raw.*****

Uri mUri = Uri.parse("android.resource://" + getPackageName() + "/"+ R.raw.*****);

19,Jni调用C/C++代码的问题

1 , java层调用C++代码
	JNI直接用包名类名方法名的方式   Java_com_md_test_Main_start(JNIEnv *env, jobject this)

	JNI通过Jni动态加载的方式构造方法名
	//列出所有的方法
	static JNINativeMethod methods[] = {
	{ "nativeCreate",					"()I", (void *) nativeCreate1 },
								空参  返回jint
		//static jint nativeCreate1(JNIEnv *env, jobject thiz) {
		//
		//	return 0;
		//}
	{ "nativeOnResume",					"()J", (void *) nativeOnResume1 },
								空参  无返回
		//static jlong nativeDestroy1(JNIEnv *env, jobject thiz) {
		//	
		//	return 0l;
		//}
	{ "nativeDestroy",					"()V", (void *) nativeDestroy1 },
								空参  无返回
		//static void nativeDestroy1(JNIEnv *env, jobject thiz) {
		//	
		//}
	{ "nativeConnect",					"(IILjava/lang/String;)I", (void *) nativeConnect },
								jint jint jstring  返回jint

	{ "nativeSetPreviewSize",				"(F)I", (void *) nativeSetPreviewSize },
								jfloat 返回jint


	{ "nativeCallback",		"(Lcom/serenegiant/usb/IStatusCallback;)I", (void *) nativeCallback },

					java中的interface 返回jint
		//static jint nativeCallback(JNIEnv *env, jobject thiz, jobject jIStatusCallback) {
		//	jobject status_callback_obj = env->NewGlobalRef(jIStatusCallback);
		//	return 0;
		//}

	//注册列出来的所有方法
	int register_uvccamera(JNIEnv *env) {
		if (registerNativeMethods(env,"com/serenegiant/usb/UVCCamera",methods, NUM_ARRAY_ELEMENTS(methods)) < 0) {
						//jni本地方法在java中的包名加类名(class_name)
			return -1;
		}
    		return 0;
	}

	jint registerNativeMethods(JNIEnv* env, const char *class_name, JNINativeMethod *methods, int num_methods) {
		int result = 0;
		jclass clazz = env->FindClass(class_name);
		if (LIKELY(clazz)) {
			int result = env->RegisterNatives(clazz, methods, num_methods);
			if (UNLIKELY(result < 0)) {
				LOGE("registerNativeMethods failed(class=%s)", class_name);
			}
		} else {
			LOGE("registerNativeMethods: class'%s' not found", class_name);
		}
		return result;
	}
	//启动注册的cpp文件
	#include "_onload.h"
	#include "utilbase.h"

	#define LOCAL_DEBUG 0

	extern int register_uvccamera(JNIEnv *env);

	jint JNI_OnLoad(JavaVM *vm, void *reserved) {
	#if LOCAL_DEBUG
    		LOGD("JNI_OnLoad");
	#endif

    		JNIEnv *env;
    		if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
        		return JNI_ERR;
    		}
    		// register native methods
    		int result = register_uvccamera(env);
		setVM(vm);
	#if LOCAL_DEBUG
    		LOGD("JNI_OnLoad:finshed:result=%d", result);
	#endif
    		return JNI_VERSION_1_6;
	}

	//启动注册的cpp头文件
	#ifndef ONLOAD_H_
	#define ONLOAD_H_

	#pragma interface

	#include <jni.h>

	#ifdef __cplusplus
	extern "C" {
	#endif

	jint JNI_OnLoad(JavaVM *vm, void *reserved);

	#ifdef __cplusplus
	}
	#endif

	#endif /* ONLOAD_H_ */

	java中调用System.loadLibrary(xx.so);vm会主动去调用JNI_OnLoad(JavaVM *vm, void *reserved)
	从而实现了所有方法的注册

2,c++代码中回调到java层

	int Java_com_ambarella_streamview_AmbaStreamSource_startWifi(JNIEnv *env, jobject this, jstring jstr)
	{
	const char *path;

	if (!sJVM) {
		jclass tmp = (*env)->FindClass(env, "com/ambarella/streamview/AmbaStreamSource");  //java中的包名类名
		jclass sCLS = (jclass)(*env)->NewGlobalRef(env, tmp);
		(*env)->GetJavaVM(env, &sJVM);
		jmethodID sMID = (*env)->GetStaticMethodID(env, sCLS, "onCallBack", "(I)V");
									//java中回调的方法   回调jint,java没有c++层的返回
									//和java调用C++刚好相反
		}
	}

	void onCallToJava(int ret){//c++中调用该方法 传入的参数就是c++层给到java层的数据
		JNIEnv *jEnv;
		(*jEnv)->CallStaticVoidMethod(jEnv, sCLS, sMID, ret);
	}
	

20,java反射机制

	public String obtainID() {
		try {
			Class<?> clazz = Class.forName("android.os.SystemProperties"); //需要反射的class类
			Object obj=clazz.newInstance(); 
			Class<?>[] param = new Class[1];  // 参数类型
            		param[0] = String.class; 
			Method med = clazz.getMethod("get",param);  
			Object o = med.invoke(obj, "ro.config.md_id_brands"); //有参函数
//			Object o1=med1.invoke(obj, null); //空参函数
			return (String)o;
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}
	}

/*****************************************   hide *****************************************/

package com.md.reflect;  
  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
  
public class ReflectUse {  
      
       public boolean UseReflect(){  
           boolean flag=false;  
            try {  
                //>>>>>>>>>>>>>>>>>>>>>>  
                //获取某个类的Class对象有三种方式(其实就是获取类Reflect),如下:  
                //方法一:利用Class.forName(String param);  
                Class cls=Class.forName("com.gzy.reflect.Reflect");  
                //方法二:使用  类名.class  
                //Class cls=Reflect.class;  
                //方法三:使用getClass()  
                //Reflect r=new Reflect();   
                //Class cls=r.getClass();  
                 
                System.out.println("-------------cls is "+cls+"-------------------------");  
                  
                //利用获取到的类的Class对象新建一个实例(相当于Reflect new了个对象)  
                Object obj=cls.newInstance();  
                  
                System.out.println("-------------obj is "+obj+"-------------------------");  
                  
                    Class[] param=new Class[1];  
                    param[0]=String.class;  
                     System.out.println("-------------param[0] is "+param[0]+"-------------------------");  
                      
                 //获取Reflect的方法,第一个参数是方法名;第二个参数是参数的类型,注意是参数的类型!       
                 Method med=cls.getMethod("setName",param);  
                 //null表示getName方法没有参数  
                 Method med1=cls.getMethod("getName", null);  
                 System.out.println("-------------med is "+med+"-------------------------");  
                   
//                 Object o=med.invoke(obj, new Object[]{"this is a reflect test."});  
                //开始调用方法,第一个参数是调用该方法的对象;第二个参数是值,即setName方法中要传入的值  
                 Object o=med.invoke(obj, "this is a reflect test.");  
                 System.out.println("-------------o is "+o+"-------------------------");  
                  
                 //方法没有 参数的话就用null表示  
                 Object o1=med1.invoke(obj, null);  
                 System.out.println("-------------o1 is "+o1+"-------------------------");  
                  
                 flag=true;  
            } catch (InstantiationException e) {  
                  
                e.printStackTrace();  
            } catch (IllegalAccessException e) {  
              
                e.printStackTrace();  
            } catch (SecurityException e) {  
              
                e.printStackTrace();  
            } catch (NoSuchMethodException e) {  
              
                e.printStackTrace();  
            } catch (IllegalArgumentException e) {  
              
                e.printStackTrace();  
            } catch (InvocationTargetException e) {  
              
                e.printStackTrace();  
            } catch (ClassNotFoundException e) {  
              
                e.printStackTrace();  
            }  
             return flag;  
       }  
}

21,关于icu编译修改

/*********** 对于Android 6.0 **********/

	/*****一下步骤正确可行,缺一不可******/

A,//// 关于修改阿拉伯语等语言下的数字显示
	在Android系统中,某些语言如阿拉伯语、波斯语、印地语、缅甸语等语言中的数字显示是使用该语言系统中的字符进行显示的,而不是使用通用的阿拉伯数字0-9显示,这样就导致时间、日期等显示比较奇怪,很多出货海外的客户要求将这些语言下的数字显示方式改为使用0-9数字显示。
	【解决方法】
	打开external/icu4c/data/misc/numberingSystems.txt文件,可以看到numberingSystems这一项中有很多语言的数字映射,比如阿拉伯语对应arab 和 arabext两个,修改desc中的数字显示为“0123456789”,这样修改就完成了。


B,////关于修改了icu/data关于修改了icu/dataml下资源文件的编译问题
	
 详细参照http://blog.csdn.net/april_12345/article/details/51281124
 
步骤如下
 
在Android 中, ICU 源码位于 external/icu/下。
    本文介绍在android下, 对icu data做了修改后,如何生成.dat文件。

    如何在icu/icu4c/source/data下修改/增加了文件,那么就需要重新build .dat文件。 这个.dat的原始文件在icu4c/source/stubdata下,名字为icudt<version><flag>.dat, 其中<version>是两个数字,表明icu版本数, <flag>是一个字符,表明该.dat文件的内部格式。例如android M上.dat文件名为icudt55l.dat.

    具体编译步骤:

    1.修改icu4c/souce/data下文件

    2.在终端中进入目录icu4c/source,:

       cd icu4c/source

    3. config 编译选项, 其中Linux代表linux, 终端中输入:
       ./runConfigureICU Linux --with-data-packaging=archive

       注:如果仅仅希望生成包含icu data的icudtxxx.dat 文件,可以使用编译选项:--with-data-packaging=archive, 即终端中输入:

       ./runConfigureICU Linux --with-data-packaging=archive
    4. 终端中输入,:

        make INCLUDE_UNI_CORE_DATA=1

        然后就可以在icu4c/source/data/out/tmp下看到新生成的icudtxxx.dat 文件
        注:必须添加INCLUDE_UNI_CORE_DATA=1, 这样相应的一些资源文件才能被加入.dat文件内, 比如uprops.icu,否则可能导致开机时找不到相应资源死机。跟着各编译选项相关的文件可参看icu4c/source/data/Makefile.in。
      
   5. push icudtxxx.dat到手机/system/usr/icu 
   
   
	/*****以下是关于修改了icu4j*****/
在android l and android M 上,新加入了icu4j, 如果修改了icu4c下的data文件,那么也要重新生成icu4j/main/shared下的两个jar文件icudata.jar 和icutzdata.jar. 
下面介绍如何生成icudata.jar 和icutzdata.jar,在以上五步的基础上:

  6. cd data (external/icu/icu4c/source/data)

  7. make ICU4J_ROOT=../../../icu4j icu4j-data-install

就可以看到在icu4j/main/shared重新生成了两个jar文件icudata.jar 和icutzdata.jar

   
关于详细内容也可以参看
android源码中的external/icu/icu4c/readme.html

android源码中的external/icu/icu4c/source/data/icu4j-readme.txt
http://userguide.icu-project.org/icudata

/****************附 以下不对,编译的文件会变小**********************/

           1. 在external/icu4c下新建临时目录icubuild,进入icubuild目录

                    $mkdir external/icu4c/icuBuild

                    $cd external/icu4c/icuBuild

           2. 执行icuConfigureRun Linux命令,生成make文件

                    $./../runConfigureICU  Linux

           3. 执行make  -j4命令,

                    $make  -j4

           4. 将生成的external/icu4c/icuBuild/data/out/tmp/icudtxxl.dat push到手机测试

                    $adb remount

                    $adb push external/icu4c/icuBuild/data/out/tmp/ icuxxl.dat  system/usr/icu/
					
					

22,关于动态权限

	private void requestPermission() {
		if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
				!= PackageManager.PERMISSION_GRANTED) {
			//??WRITE_EXTERNAL_STORAGE??
			ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
					1);
		}
	}
	
	
	import android.support.v4.app.ActivityCompat;
	import android.support.v4.content.ContextCompat;
	
	
	注::   如果targetSdkVersion < 23 (Android6.0) 只要你的manifest中加入了读写的权限,	  
			调用checkSelfPermission,不管用户是否取消授权,checkSelfPermission的返回值始终为PERMISSION_GRANTED (权限已获取)  
			不然就会要动态给予权限      Android6.0以下的版本(含)
	
	<uses-sdk
        android:minSdkVersion="21"
        android:targetSdkVersion="21" />
		
	6.0以下用PermissionChecker.checkSelfPermission   6.0以上用:ContextCompat.checkSelfPermission 或者 Context.checkSelfPermission
		
		
	public boolean selfPermissionGranted(String permission) {
        // For Android < Android M, self permissions are always granted.
        boolean result = true;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

            if (targetSdkVersion >= Build.VERSION_CODES.M) {
                // targetSdkVersion >= Android M, we can
                // use Context#checkSelfPermission
                result = context.checkSelfPermission(permission)
                        == PackageManager.PERMISSION_GRANTED;
            } else {
                // targetSdkVersion < Android M, we have to use PermissionChecker
                result = PermissionChecker.checkSelfPermission(context, permission)
                        == PermissionChecker.PERMISSION_GRANTED;
            }
        }

        return result;
    }
	In order to obtain target Sdk Version you can use:

    try {
        final PackageInfo info = context.getPackageManager().getPackageInfo(
                context.getPackageName(), 0);
        targetSdkVersion = info.applicationInfo.targetSdkVersion;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
		
		
	参考:::http://blog.csdn.net/qq_15364915/article/details/53425414

23,开机动画延迟问题

//
	1 代码位置:
		/home/********/frameworks/base/cmds/bootanimation/BootAnimation.cpp
	checkExit()中
		
		   if (exitnow) {
	 		usleep(2000000);//重点调这里(微秒)
        		requestExit();
        		if (mAudioPlayer != NULL) {
            			mAudioPlayer->requestExit();
        		}
    		}
			

24,关于获取framework-res.apk中资源问题

public int getStatusBarHeight() {
        if (mNaturalBarHeight < 0) {
            final Resources res = mContext.getResources();
            mNaturalBarHeight =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); // 获取资源文件中的数据
        }
        return mNaturalBarHeight;
    }



2, 如果自己要在res中添加自己的资源项
	1>需要在symbols.xml  中加入改dimen的对应的name  
	例 :<java-symbol type="string"  name="config_wifi_random_mac_oui" />
	make update-api
	引用的是com.android.internal.R下的资源  //只能在系统层引用

	2>添加在public.xml中的资源项
	make update-api
	引用的是android.R下的资源   //外部层和内部层都能引用
		
	详见: 收藏 ---》Android系统开发---》framework---》Android Framework中新加res资源方式(
http://m.blog.csdn.net/article/details?id=41961925)

25,关于系统Theme和主题的修改

/********************************  系统中的各种页面打开关闭动画****************************/

	/home/******/frameworks/base/core/res/res/values/styles.xml

	1,  主要的Animation.Activity 控制各种页面  还有其他的Animation    
	<!-- Base style for animations.  This style specifies no animations. -->
    	<style name="Animation" />   //没有动画--默认

    	<!-- Standard animations for a full-screen window or activity.         //修改前的Animation.Activity。
	 	<item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
        	<item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
        	<item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
        	<item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
        	<item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
        	<item name="taskOpenExitAnimation">@anim/task_open_exit</item>
        	<item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item>
        	<item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
        	<item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
        	<item name="taskCloseExitAnimation">@anim/task_close_exit</item>
        	<item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
        	<item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
        	<item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
        	<item name="taskToBackExitAnimation">@anim/task_close_exit</item>
        	<item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
        	<item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
        	<item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
        	<item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
        	<item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
        	<item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
       	 	<item name="fragmentOpenEnterAnimation">@animator/fragment_open_enter</item>
        	<item name="fragmentOpenExitAnimation">@animator/fragment_open_exit</item>
        	<item name="fragmentCloseEnterAnimation">@animator/fragment_close_enter</item>
        	<item name="fragmentCloseExitAnimation">@animator/fragment_close_exit</item>
        	<item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
        	<item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item> -->

    	<style name="Animation.Activity">     // 修改后的Animation.Activity
        	<item name="activityOpenEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="activityOpenExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="activityCloseEnterAnimation">@null</item>
        	<item name="activityCloseExitAnimation">@null</item>
        	<item name="taskOpenEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="taskOpenExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item>
        	<item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
        	<item name="taskCloseEnterAnimation">@null</item>
        	<item name="taskCloseExitAnimation">@null</item>
        	<item name="taskToFrontEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="taskToFrontExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="taskToBackEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="taskToBackExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
        	<item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
        	<item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
        	<item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
        	<item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
        	<item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
        	<item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
        	<item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
        	<item name="fragmentOpenEnterAnimation">@animator/fragment_open_enter</item>
        	<item name="fragmentOpenExitAnimation">@animator/fragment_open_exit</item>
        	<item name="fragmentCloseEnterAnimation">@animator/fragment_close_enter</item>
        	<item name="fragmentCloseExitAnimation">@animator/fragment_close_exit</item>
        	<item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
        	<item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item>   
  	</style>


/*********************************************   系统各种主题样式(Theme)     ****************************************/

	/home/*******/frameworks/base/core/res/res/values/themes.xml

	<style name="Theme.Black">
        	<item name="windowBackground">@color/black</item>
        	<item name="colorBackground">@color/black</item>
        	<item name="windowAnimationStyle">@style/Animation.Activity</item>  //后加入,引入页面打开动画
        	<item name="windowContentOverlay">@null</item>                  //
    	</style>

    	<!-- Variant of {@link #Theme_Black} with no title bar -->
    	<style name="Theme.Black.NoTitleBar">
        	<item name="windowNoTitle">true</item>
        	<item name="windowAnimationStyle">@style/Animation.Activity</item>
        	<item name="windowContentOverlay">@null</item>
    	</style>


	注::: 关于windowContentOverlay的作用
		相信每个app都需要一个闪屏页 就是一个开始页面  对于新手来说 可能就直接一个activity 弄个背景图片 ,细心地同学也许会发现 每次启动 应用程序的时候 一开始显示的不是那个设置的背景图片  而是白色或黑色背景 可能时间很短 这是为什么呢 。再看看其他市面上的应用都是一开始就是显示背景图片 而没有白色背景  研究了很久 			其实 设置一下主		题就行了  给activity设置主题 其中有一个属性
		

		用到的Theme<style name="Theme.Translucent.NoTitleBar">  
        		<item name="android:windowNoTitle">true</item>  
        		<item name="android:windowContentOverlay">@null</item>  
    		 </style> 

26,关于长按电源键的相关操作

/************************  关机 、 重启  、...../

1 , PhoneWindowManger --> GlobalActions(mWindowManagerFuncs.reboot()) -->  WindowMangerService(reboot()) --> ShutDownThread(reboot(mContext,null ,true/false))

/*****/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManger.java

/*****/frameworks/base/services/core/java/com/android/server/wm/WindowMangerService.java

和截屏按键、HOME按键的处理流程类似,电源按键由于也是系统级别的按键,所以对其的事件处理逻辑是和截屏按键、HOME按键类似,不在某一个App中,而是在PhoneWindowManager的dispatchUnhandledKey方法中。所以和前面两篇类似,这里我们也是从PhoneWindowManager的dispatchUnhandledKey方法开始我们今天电源开关机按键的事件流程分析。

下面首先看一下dispatchUnhandledKey方法的实现逻辑:

public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
        ...
        KeyEvent fallbackEvent = null;
        if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            final KeyCharacterMap kcm = event.getKeyCharacterMap();
            final int keyCode = event.getKeyCode();
            final int metaState = event.getMetaState();
            final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN
                    && event.getRepeatCount() == 0;

            // Check for fallback actions specified by the key character map.
            final FallbackAction fallbackAction;
            if (initialDown) {
                fallbackAction = kcm.getFallbackAction(keyCode, metaState);
            } else {
                fallbackAction = mFallbackActions.get(keyCode);
            }

            if (fallbackAction != null) {
                if (DEBUG_INPUT) {
                    Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode
                            + " metaState=" + Integer.toHexString(fallbackAction.metaState));
                }

                final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
                fallbackEvent = KeyEvent.obtain(
                        event.getDownTime(), event.getEventTime(),
                        event.getAction(), fallbackAction.keyCode,
                        event.getRepeatCount(), fallbackAction.metaState,
                        event.getDeviceId(), event.getScanCode(),
                        flags, event.getSource(), null);

                if (!interceptFallback(win, fallbackEvent, policyFlags)) {
                    fallbackEvent.recycle();
                    fallbackEvent = null;
                }

                if (initialDown) {
                    mFallbackActions.put(keyCode, fallbackAction);
                } else if (event.getAction() == KeyEvent.ACTION_UP) {
                    mFallbackActions.remove(keyCode);
                    fallbackAction.recycle();
                }
            }
        }
        ...
        return fallbackEvent;
    }
我们知道关于系统按键的处理逻辑被下放到了interceptFallback方法中,所以我们继续看一下interceptFallback方法的实现逻辑。

private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
        int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
        if ((actions & ACTION_PASS_TO_USER) != 0) {
            long delayMillis = interceptKeyBeforeDispatching(
                    win, fallbackEvent, policyFlags);
            if (delayMillis == 0) {
                return true;
            }
        }
        return false;
    }

	
通过分析interceptFallback方法的源码,我们知道关于电源按键的处理逻辑在interceptKeyBeforeQueueing方法中,所以我们需要继续看一下interceptKeyBeforeQueueing方法中关于电源按键的处理逻辑。

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        ...
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactive);
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }
            ...

        return result;
    }

这里我们重点看一下电源按键的处理事件,可以发现当电源按键按下的时候我们调用了interceptPowerKeyDown方法,可以看出,这个方法就是处理电源事件的了,既然如此,我们继续看一下interceptPowerKeyDown方法的执行逻辑。

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        ...
        // Latch power key state to detect screenshot chord.
        if (interactive && !mScreenshotChordPowerKeyTriggered
                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
            mScreenshotChordPowerKeyTriggered = true;
            mScreenshotChordPowerKeyTime = event.getDownTime();
            interceptScreenshotChord();
        }

        // Stop ringing or end call if configured to do so when power is pressed.
        TelecomManager telecomManager = getTelecommService();
        boolean hungUp = false;
        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing Power while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger();
            } else if ((mIncallPowerBehavior
                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall() && interactive) {
                // Otherwise, if "Power button ends call" is enabled,
                // the Power button will hang up any current active call.
                hungUp = telecomManager.endCall();
            }
        }

        // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
                || mScreenshotChordVolumeUpKeyTriggered;
        if (!mPowerKeyHandled) {
            if (interactive) {
                // When interactive, we're already awake.
                // Wait for a long press or for the button to be released to decide what to do.
                if (hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                }
            } else {
                wakeUpFromPowerKey(event.getDownTime());

                if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                    mBeganFromNonInteractive = true;
                } else {
                    final int maxCount = getMaxMultiPressPowerCount();

                    if (maxCount <= 1) {
                        mPowerKeyHandled = true;
                    } else {
                        mBeganFromNonInteractive = true;
                    }
                }
            }
        }
    }

	
这里我们重点看一下if(interactive)分支,在这里我们发送一个一个异步消息,并且msg的what为MSG_POWER_LONG_PRESS,即长按电源事件的异步消息,所以我们看一下mHandler的handleMessage方法对该what消息的处理逻辑。

case MSG_POWER_LONG_PRESS:
                    powerLongPress();
                    break;

					
我们可以发现在mHandler的handleMessage方法中当msg的what为MSG_POWER_LONG_PRESS时我们调用了powerLongPress方法,这个方法应该就是处理电源按键长按的逻辑,下面我们来看一下powerLongPress方法的实现。

private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                performAuditoryFeedbackForAccessibilityIfNeed();
            }
            showGlobalActionsInternal();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:
        case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
            break;
        }
    }

	
可以发现这里有四个switch分之,其中第一个什么都不做直接break掉,第二个case则需要弹出选择操作界面,比如:飞行模式,开关机,静音模式,重新启动等,这里可以参看一下小米手机的关机界面: 
这里写图片描述

然后第三第四个case分之则是直接调用关机方法,这里我们先看第二个case,看看系统是如何显示出关机操作界面的。那我们看一下showGlobalActionsInternal方法的实现逻辑。

void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        if (mGlobalActions == null) {
            mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
        }
        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
        if (keyguardShowing) {
            // since it took two seconds of long press to bring this up,
            // poke the wake lock so they have some time to see the dialog.
            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
        }
    }

可以发现我们首先调用了sendCloseSystemWindows方法,知道该方法用于关机系统弹窗,比如输入法,壁纸等。然后我们创建了一个GlobalActions对象,并调用了其showDialog方法,通过分析源码,我们发现该方法就是用于显示长按电源按键弹出操作界面的,我们首先看一下GlobalActions的构造方法:

public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
        mContext = context;
        mWindowManagerFuncs = windowManagerFuncs;
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.getService(DreamService.DREAM_SERVICE));

        // receive broadcasts
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
        context.registerReceiver(mBroadcastReceiver, filter);

        ConnectivityManager cm = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);
        mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);

        // get notified of phone state changes
        TelephonyManager telephonyManager =
                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
        mContext.getContentResolver().registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
                mAirplaneModeObserver);
        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        mHasVibrator = vibrator != null && vibrator.hasVibrator();

        mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_useFixedVolume);
    }

可以看到在GlobalActions对象的构造方法中我们主要用于初始化其成员变量,由于我们的电源长按操作界面是一个全局页面,所以这里自定义了一个Window对象,下面我们看一下GlobalActions的showDialog方法。

public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        mKeyguardShowing = keyguardShowing;
        mDeviceProvisioned = isDeviceProvisioned;
        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
            // Show delayed, so that the dismiss of the previous dialog completes
            mHandler.sendEmptyMessage(MESSAGE_SHOW);
        } else {
            handleShow();
        }
    }

可以看到在showDialog方法中我们首先判断mDialog是否为空,若为空则发送msg的what为MESSAGE_SHOW的异步消息,否则调用handleShow方法,而这里的mDialog是一个类型为GlobalActionsDialog的变量,由于我们的mDialog为空,所以下面我们看一下handleShow方法。

private void handleShow() {
        awakenIfNecessary();
        mDialog = createDialog();
        prepareDialog();

        // If we only have 1 item and it's a simple press action, just do this action.
        if (mAdapter.getCount() == 1
                && mAdapter.getItem(0) instanceof SinglePressAction
                && !(mAdapter.getItem(0) instanceof LongPressAction)) {
            ((SinglePressAction) mAdapter.getItem(0)).onPress();
        } else {
            WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
            attrs.setTitle("GlobalActions");
            mDialog.getWindow().setAttributes(attrs);
            mDialog.show();
            mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND);
        }
1

在方法体中我们调用了createDialog方法,创建了GlobalActionsDialog类型的mDialog,这里我们看一下createDialog的实现方法。

private GlobalActionsDialog createDialog() {
        ...
        mAirplaneModeOn = new ToggleAction(
                R.drawable.ic_lock_airplane_mode,
                R.drawable.ic_lock_airplane_mode_off,
                R.string.global_actions_toggle_airplane_mode,
                R.string.global_actions_airplane_mode_on_status,
                R.string.global_actions_airplane_mode_off_status) {

            void onToggle(boolean on) {
                if (mHasTelephony && Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
                    mIsWaitingForEcmExit = true;
                    // Launch ECM exit dialog
                    Intent ecmDialogIntent =
                            new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    mContext.startActivity(ecmDialogIntent);
                } else {
                    changeAirplaneModeSystemSetting(on);
                }
            }

            @Override
            protected void changeStateFromPress(boolean buttonOn) {
                if (!mHasTelephony) return;

                // In ECM mode airplane state cannot be changed
                if (!(Boolean.parseBoolean(
                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
                    mState = buttonOn ? State.TurningOn : State.TurningOff;
                    mAirplaneState = mState;
                }
            }

            public boolean showDuringKeyguard() {
                return true;
            }

            public boolean showBeforeProvisioning() {
                return false;
            }
        };
        onAirplaneModeChanged();

        mItems = new ArrayList<Action>();
        String[] defaultActions = mContext.getResources().getStringArray(
                com.android.internal.R.array.config_globalActionsList);

        ArraySet<String> addedKeys = new ArraySet<String>();
        for (int i = 0; i < defaultActions.length; i++) {
            String actionKey = defaultActions[i];
            if (addedKeys.contains(actionKey)) {
                // If we already have added this, don't add it again.
                continue;
            }
            if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
                mItems.add(new PowerAction());
            } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
                mItems.add(mAirplaneModeOn);
            } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
                if (Settings.Global.getInt(mContext.getContentResolver(),
                        Settings.Global.BUGREPORT_IN_POWER_MENU, 0) != 0 && isCurrentUserOwner()) {
                    mItems.add(getBugReportAction());
                }
            } else if (GLOBAL_ACTION_KEY_SILENT.equals(actionKey)) {
                if (mShowSilentToggle) {
                    mItems.add(mSilentModeAction);
                }
            } else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
                if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
                    addUsersToMenu(mItems);
                }
            } else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
                mItems.add(getSettingsAction());
            } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                mItems.add(getLockdownAction());
            } else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
                mItems.add(getVoiceAssistAction());
            } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
                mItems.add(getAssistAction());
            } else {
                Log.e(TAG, "Invalid global action key " + actionKey);
            }
            // Add here so we don't add more than one.
            addedKeys.add(actionKey);
        }

        mAdapter = new MyAdapter();

        AlertParams params = new AlertParams(mContext);
        params.mAdapter = mAdapter;
        params.mOnClickListener = this;
        params.mForceInverseBackground = true;

        GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
        dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.

        dialog.getListView().setItemsCanFocus(true);
        dialog.getListView().setLongClickable(true);
        dialog.getListView().setOnItemLongClickListener(
                new AdapterView.OnItemLongClickListener() {
                    @Override
                    public boolean onItemLongClick(AdapterView<?> parent, View view, int position,
                            long id) {
                        final Action action = mAdapter.getItem(position);
                        if (action instanceof LongPressAction) {
                            return ((LongPressAction) action).onLongPress();
                        }
                        return false;
                    }
        });
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        dialog.setOnDismissListener(this);

        return dialog;
    }

方法体的内容比较长,我们看重点的内容,首先我们通过调用mContext.getResources().getStringArray(com.Android.internal.R.array.config_globalActionsList)获得操作列表,这里可能包含:飞行模式、开关机、静音模式、重启等等,然后我们轮训操作列表,并添加相应的Action最后我们将这个操作列表保存到Dialog的adapter中并返回该dialog,然后我们回到我们刚刚的handleShow方法,在得到返回的dialog之后我们调用了dialog的show方法,这样我们就显示出了电源长按操作界面,比如小米的界面: 
这里写图片描述

好吧,继续我们的分析,当我们长按电源按键弹出操作弹窗之后,这时候点击关机是怎么样的流程呢?我们发现在createDialog方法中关机操作adapter的item,我们添加了:

mItems.add(new PowerAction());

这样不难发现我们对关机按钮的操作封装在了PowerAction中,所以我们继续看一下PowerAction的实现。

private final class PowerAction extends SinglePressAction implements LongPressAction {
        private PowerAction() {
            super(com.android.internal.R.drawable.ic_lock_power_off,
                R.string.global_action_power_off);
        }

        @Override
        public boolean onLongPress() {
            UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
            if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
                mWindowManagerFuncs.rebootSafeMode(true);
                return true;
            }
            return false;
        }

        @Override
        public boolean showDuringKeyguard() {
            return true;
        }

        @Override
        public boolean showBeforeProvisioning() {
            return true;
        }

        @Override
        public void onPress() {
            // shutdown by making sure radio and power are handled accordingly.
            mWindowManagerFuncs.shutdown(false /* confirm */);
        }
    }

/**
可以发现在PowerAction类的成员函数onPress方法中我们调用了mWindowManagerFuncs.showdown方法,而这个方法也就是开始执行我们的关机操作了,那么这里的mWindowManagerFuncs又是什么呢?它是在什么时候赋值的呢?通过分析我们发现这里的mWindowManagerFuncs成员变量是在GlobalActions的构造方法中赋值的。

public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) {
        ...
        mWindowManagerFuncs = windowManagerFuncs;
        ...
}

好吧,回到我们的PhoneWindowManager,早构造GlobalActions时,直接传递的是PhoneWindowManager的成员变量mWindowManagerFuncs,那么PhoneWindowManager的mWindowManagerFuncs成员变量又是何时被赋值的呢?通过分析源码我们能够看到PhoneWindowManager的mWindowManagerFuncs变量是在PhoneWindowManager的init方法中初始化的,好吧,再次查找PhoneWindowManager的init方法是何时被调用的。

经过查找终于在WindowManagerService中我们找到了PhoneWindowManager的init方法的调用。

private void initPolicy() {
        UiThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());

                mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
            }
        }, 0);
    }

这里的mPolicy就是一个PhoneWindowManager的实力,可以发现这里的init方法中mWindowManagerFuncs传递的就是一个WindowManagerService的实例,O(∩_∩)O哈哈~,让我们好找。

然么在PowerAction的onPress方法中调用的mWindowManagerFuncs.shutdown(false /* confirm */);
//方法,实际上调用的就是WindowManagerService的shutdown方法,这样我们继续看一下WindowManagerService的shutdown方法的实现。

/**@Override
    public void shutdown(boolean confirm) {
        ShutdownThread.shutdown(mContext, confirm);
    }

好吧,这里很简单就是直接调用了ShutdownThread的shutdown方法,看样子这里就是执行关机操作的封装了,继续看一下ShutdownThread的shutdown方法。

public static void shutdown(final Context context, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;
        shutdownInner(context, confirm);
    }

可以看到在ShutdownThread的shutdown方法中代码很简单,具体的操作下发到了shutdownInner方法中,那么我们继续看一下shutdownInner方法的实现。

static void shutdownInner(final Context context, boolean confirm) {
        // ensure that only one thread is trying to power down.
        // any additional calls are just returned
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Request to shutdown already running, returning.");
                return;
            }
        }

        final int longPressBehavior = context.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
        final int resourceId = mRebootSafeMode
                ? com.android.internal.R.string.reboot_safemode_confirm
                : (longPressBehavior == 2
                        ? com.android.internal.R.string.shutdown_confirm_question
                        : com.android.internal.R.string.shutdown_confirm);

        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

        if (confirm) {
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            sConfirmDialog = new AlertDialog.Builder(context)
                    .setTitle(mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off)
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                        }
                    })
                    .setNegativeButton(com.android.internal.R.string.no, null)
                    .create();
            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);
            sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
            sConfirmDialog.show();
        } else {
            beginShutdownSequence(context);
        }
    }

可以看到方法体中,首先判断若用户点击了关机按键是否弹出确认框,若弹出则弹出关机确认框,若不需要确认,则直接调用beginShutdownSequence方法,执行关机操作。而在关机确认框中我们的确认按钮也是执行了beginShutdownSequence方法,所以我们继续看一下关机方法beginShutdownSequence。

private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }
        ...
        if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
            mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
            if (mRebootUpdate) {
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
                pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_update_prepare));
                pd.setMax(100);
                pd.setProgressNumberFormat(null);
                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                pd.setProgress(0);
                pd.setIndeterminate(false);
            } else {
                // Factory reset path. Set the dialog message accordingly.
                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
                pd.setMessage(context.getText(
                        com.android.internal.R.string.reboot_to_reset_message));
                pd.setIndeterminate(true);
            }
        } else {
            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
            pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
            pd.setIndeterminate(true);
        }
        pd.setCancelable(false);
        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        pd.show();

        sInstance.mProgressDialog = pd;
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        // make sure we never fall asleep again
        sInstance.mCpuWakeLock = null;
        try {
            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mCpuWakeLock = null;
        }

        // also make sure the screen stays on for better user experience
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }

        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

在方法beginShutdownSequence中我们首先初始化了一个Process的dialog,该dialog用于显示关机界面,然后我们调用了sInstance.start方法,再往下的方法中就是真正的shutdown方法的实现,同时也是native方法,我们这里就不做过得解读了。。。

总结:

电源按键是系统按键,所以对电源按键的处理逻辑也是在PhoneWindowManager的dispatchUnhandledKey方法中;

在PhoneWindowManager的dispatchUnhandleKey方法处理Power按键之后会首先显示系统操作弹窗,一般包括但不限于:飞行模式,静音模式,重新启动,关机等;

当用户点击关机按钮是调用的是WindowManagerService.shutdown方法,而内部调用的是ShutdownThread.shutdown方法;

希望能帮到各位。。。

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章