車載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方法;

希望能幫到各位。。。

 

 

 

 

 

 

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