手机卫士day09
day09
清除來電記錄
代碼掛斷電話后,被掛斷的號碼仍然會進入通話記錄中, 我們需要將這種記錄刪除.查看數據庫contacts2中的表calls/*** 刪除通話記錄*/private void deleteCallLog(String number) {getContentResolver().delete(Uri.parse("content://call_log/calls"),"number=?", new String[] {number});}注意加權限: <uses-permission android:name="android.permission.READ_CALL_LOG"/><uses-permission android:name="android.permission.WRITE_CALL_LOG"/>- 通過內容觀察者,解決通話記錄刪除失敗的問題系統在往通話記錄的數據庫中插入數據時是異步邏輯,所以當數據庫還沒來得及添加電話日志時,我們就執行了刪除日志的操作,從而導致刪除失敗,為了避免這個問題,可以監聽數據庫變化,當數據庫發生變化后,我們才執行刪除操作,從而解決這個問題/*** 內容觀察者* @author Kevin**/class MyContentObserver extends ContentObserver {private String incomingNumber;public MyContentObserver(Handler handler, String incomingNumber) {super(handler);this.incomingNumber = incomingNumber;}/*** 當數據庫發生變化時,回調此方法*/@Overridepublic void onChange(boolean selfChange) {System.out.println("call log changed...");//刪除日志deleteCallLog(incomingNumber);//刪除完日志后,注銷內容觀察者getContentResolver().unregisterContentObserver(mObserver);}}------------------------------//監聽到來電時,注冊內容觀察者mObserver = new MyContentObserver(new Handler(),incomingNumber);//注冊內容觀察者getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), true,mObserver);------------------------------注意:補充Android2.3模擬器上需要多加權限<uses-permission android:name="android.permission.WRITE_CONTACTS"/>短信備份
查看短信數據庫
data/data/com.android.provider.telephony/databases/mmssms.db address 短信收件人發件人地址 date 短信接收的時間 type 1 發進來短信 2 發出去短信 read 1 已讀短信 0 未讀短信 body 短信內容讀取短信數據庫內容
查看系統源碼,找到uri地址:packages\provider\platform_packages_providers_telephonyprovider-masterUri uri = Uri.parse("content://sms/");// 所有短信 Cursor cursor = ctx.getContentResolver().query(uri,new String[] { "address", "date", "type", "body" }, null, null,null);遍歷cursor,獲取短信信息注意權限: <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.WRITE_SMS"/>將短信內容序列化為xml文件
sms.xml <?xml version="1.0" encoding="utf-8"?> <smss><sms><address>5556</address><date>10499949433</date><type>1</type><body>wos shi haoren</body></sms><sms><address>13512345678</address><date>1049994889433</date><type>2</type><body>hell world hei ma</body></sms> </smss>------------------------------XmlSerializer serializer = Xml.newSerializer();// 初始化xml序列化工具 serializer.setOutput(new FileOutputStream(output), "utf-8");//設置輸出流 /** startDocument(String encoding, Boolean standalone)encoding代表編碼方式* standalone 用來表示該文件是否關聯其它外部的約束文件。 若值是 ”yes” 表示沒有關聯外部規則文件,若值是 ”no”* 則表示有關聯外部規則文件。默認值是 “yes”。* <?xml version="1.0" encoding="utf-8" standalone=true ?>* * 參數2傳null時,不會生成standalone=true/false的語句*/ serializer.startDocument("utf-8", null);// 生成xml頂欄描述語句<?xml// version="1.0"// encoding="utf-8"?> serializer.startTag(null, "smss");//起始標簽 serializer.text(body);// 設置內容 serializer.endTag(null, "smss");//結束標簽 serializer.endDocument();//結束xml文檔------------------------------AToolsActivity.java/*** 短信備份*/ public void smsBackup(View view) {if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {try {SmsUtils.smsBackup(this,new File(Environment.getExternalStorageDirectory(),"sms.xml"));Toast.makeText(this, "備份成功!", Toast.LENGTH_SHORT).show();} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "備份失敗!", Toast.LENGTH_SHORT).show();}} else {Toast.makeText(this, "沒有檢測到sdcard!", Toast.LENGTH_SHORT).show();} }異步備份短信,并顯示進度條
mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage("正在備份短信..."); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);//設置顯示風格,此風格將展示一個進度條 mProgressDialog.show();將ProgressDialog的引用傳遞給工具類,在工具類中更新進度 SmsUtils.smsBackup(AToolsActivity.this, new File(Environment.getExternalStorageDirectory(),"sms.xml"), mProgressDialog);--------------------------------//短信工具類中更新進度條的邏輯 progressDialog.setMax(cursor.getCount());// 設置進度條最大值Thread.sleep(500);//為了方便看效果,故意延時1秒鐘 progress++; progressDialog.setProgress(progress);//更新進度--------------------------------模擬需求變動的情況 1. A負責短信備份界面, B負責短信工具類 2. 將ProgressDialog改動為ProgressBar, 需要A通知B改動 3. 又將ProgressBar改回ProgressDialog, 需要A通知B改動 4. 既有ProgressBar,又要求有ProgressDialog, 需要A通知B改動問題: B除了負責底層業務邏輯之外,額外還需要幫A處理界面邏輯,如何實現A和B的解耦?使用回調接口通知進度,優化代碼,實現解耦
/*** 短信備份回調接口* @author Kevin**/ public interface SmsBackupCallback {/*** 備份之前獲取短信總數* @param total*/public void preSmsBackup(int total);/*** 備份過程中實時獲取備份進度* @param progress*/public void onSmsBackup(int progress); }----------------------------SmsUtils.smsBackup(AToolsActivity.this, new File(Environment.getExternalStorageDirectory(),"sms.xml"), new SmsBackupCallback() {@Overridepublic void preSmsBackup(int total) {mProgressDialog.setMax(total);}@Overridepublic void onSmsBackup(int progress) {mProgressDialog.setProgress(progress);}});
短信還原(介紹)
應用管理器(AppManagerActivity)
- 介紹金山衛士的應用管理器
- 參考金山衛士,編寫布局文件
計算內置存儲空間和sdcard剩余空間
/*** 獲取剩余空間* * @param path* @return*/ private String getAvailSpace(String path) {StatFs stat = new StatFs(path);// Integer.MAX_VALUE;// int最大只能表示到2G, 在一些高端手機上不足夠接收大于2G的容量,所以可以用long來接收, 相乘的結果仍是long類型long blocks = stat.getAvailableBlocks();// 獲取可用的存儲塊個數long blockSize = stat.getBlockSize();// 獲取每一塊的大小return Formatter.formatFileSize(this, blocks * blockSize);// 將字節轉化為帶有容量單位的字符串 }//獲取內存的地址 Environment.getDataDirectory().getAbsolutePath() //獲取sdcard的地址 Environment.getExternalStorageDirectory().getAbsolutePath()獲取已安裝的應用列表
/*** 應用信息封裝* * @author Kevin* */ public class AppInfo {public String name;// 名稱public String packageName;// 包名public Drawable icon;// 圖標public boolean isUserApp;// 是否是用戶程序public boolean isRom;// 是否安裝在內置存儲器中@Overridepublic String toString() {return "AppInfo [name=" + name + ", packageName=" + packageName + "]";} }-------------------------------AppInfoProvider.java/*** 獲取已安裝的應用信息* @param ctx*/ public static ArrayList<AppInfo> getAppInfos(Context ctx) {ArrayList<AppInfo> infoList = new ArrayList<AppInfo>();PackageManager pm = ctx.getPackageManager();List<PackageInfo> packages = pm.getInstalledPackages(0);// 獲取已經安裝的所有包for (PackageInfo packageInfo : packages) {AppInfo info = new AppInfo();String packageName = packageInfo.packageName;// 獲取包名Drawable icon = packageInfo.applicationInfo.loadIcon(pm);// 獲取圖標String name = packageInfo.applicationInfo.loadLabel(pm).toString();// 獲取名稱info.packageName = packageName;info.icon = icon;info.name = name;infoList.add(info);}return infoList; }進行單元測試,打印應用列表Android的應用程序安裝位置
pc電腦默認安裝在C:\Program Files Android 的應用安裝在哪里呢,如果是用戶程序,安裝在data/app/目錄下, 系統自帶應用安裝在system/app/目錄下安裝Android軟件 做兩件事 A:把APK拷貝到data/app/目錄下B:把安裝包信息寫到data/system/目錄下兩個文件packages.list 和 packages.xml安裝包信息在data/system/ Packages.list 里面的0 表示系統應用 1 表示用戶應用 Packages.xml是存放應用的一些權限信息的;判斷是系統應用還是用戶應用
- 解釋標識左移幾位的效果public static final int FLAG_SYSTEM = 1<<0;
- 不同標識可以加起來一起使用, 加起來的結果和特定標識進行與運算,通過計算結果可以知道具不具備該特定標識的相關功能, 這種方式叫做狀態機
- 玩游戲距離: 喝藥水,加功能(加血,加攻擊力,加防御,加魔法值),可以通過狀態機來表示該藥水具備哪些特性
實際開發的機頂盒舉例:{“cctv1”:true,”cctv2”:false,”cctv3”:true}->{“flag”:101}, 可以節省流量
if ((flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM) {info.isUserApp = false;// 系統應用 } else {info.isUserApp = true;//用戶應用 }if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == ApplicationInfo.FLAG_EXTERNAL_STORAGE) {info.isRom = false;// 安裝位置是sdcard } else {info.isRom = true;//安裝位置是內置存儲器 }
ListView列表展現
- 仿照金山衛士編寫item布局
異步加載應用列表數據,并顯示進度條
加載應用列表有時會比較耗時,最好放在子線程執行. 加載期間顯示加載中的布局<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" ><ListViewandroid:id="@+id/lv_list"android:layout_width="match_parent"android:layout_height="match_parent" > </ListView><LinearLayoutandroid:id="@+id/ll_loading"android:visibility="gone"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:paddingBottom="50dp"android:orientation="vertical" ><ProgressBarandroid:id="@+id/progressBar1"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/textView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="正在加載應用列表..." /> </LinearLayout><TextViewandroid:id="@+id/tv_head"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#FF888888"android:textColor="#fff"android:text="用戶程序(5)"/>清單文件中注冊安裝位置
manifest根標簽具有這樣的屬性,用來指定apk的安裝位置, 缺省值是手機內存 android:installLocation="auto", 可以修改為auto, 表示優先使用手機內存, 如果內存不夠,再使用sdcard. 不建議強制安裝在sdcard,因為某些手機沒有sdcard,會導致安裝失敗.設置完成后,就可以在系統應用管理中,移動apk的安裝位置了.
復雜ListView的展現方式
核心思想: 重寫BaseAdapter自帶的getItemViewType方法來返回item類型,重寫getViewTypeCount方法返回類型個數//應用信息適配器 class AppInfoAdapter extends BaseAdapter {/*** 返回總數量,包括兩個標題欄*/@Overridepublic int getCount() {return 1 + mUserAppList.size() + 1 + mSystemAppList.size();}/*** 返回當前的對象*/@Overridepublic AppInfo getItem(int position) {if (position == 0 || position == 1 + mUserAppList.size()) {//判斷是否是標題欄return null;}AppInfo appInfo;if (position < mUserAppList.size() + 1) {//判斷是否是用戶應用appInfo = mUserAppList.get(position - 1);} else {//系統應用appInfo = mSystemAppList.get(position - mUserAppList.size() - 2);}return appInfo;}@Overridepublic long getItemId(int position) {return position;}/*** 返回當前view的類型*/@Overridepublic int getItemViewType(int position) {if (position == 0 || position == 1 + mUserAppList.size()) {return 1;// 標題欄} else {return 0;// 應用信息}}/*** 返回當前item的類型個數*/@Overridepublic int getViewTypeCount() {return 2;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {int type = getItemViewType(position);//獲取item的類型if (convertView == null) {//初始化convertViewswitch (type) {case 1://標題convertView = new TextView(AppManagerActivity.this);((TextView) convertView).setTextColor(Color.WHITE);((TextView) convertView).setBackgroundColor(Color.GRAY);break;case 0://應用信息ViewHolder holder;convertView = View.inflate(AppManagerActivity.this,R.layout.list_appinfo_item, null);holder = new ViewHolder();holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);holder.tvLocation = (TextView) convertView.findViewById(R.id.tv_location);convertView.setTag(holder);break;default:break;}}//根據類型來更新view的顯示內容switch (type) {case 1:if (position == 0) {((TextView) convertView).setText("用戶程序("+ mUserAppList.size() + ")");} else {((TextView) convertView).setText("系統程序("+ mUserAppList.size() + ")");}break;case 0:ViewHolder holder = (ViewHolder) convertView.getTag();AppInfo appInfo = getItem(position);holder.ivIcon.setImageDrawable(appInfo.icon);holder.tvName.setText(appInfo.name);if (appInfo.isRom) {holder.tvLocation.setText("手機內存");} else {holder.tvLocation.setText("外置存儲卡");}break;default:break;}return convertView;} }
PopupWindow使用
專門寫一個Demo,用于PopupWindow的演示/*** 顯示彈窗* * @param view*/ public void showPopupWindow(View view) {TextView contentView = new TextView(this);contentView.setText("我是彈窗哦!");contentView.setTextColor(Color.RED);PopupWindow popup = new PopupWindow(contentView, 100, 100, true);//設置尺寸及獲取焦點popup.setBackgroundDrawable(new ColorDrawable(Color.BLUE));//設置背景顏色// popup.showAtLocation(rlRoot, Gravity.LEFT + Gravity.TOP, 0,// 0);//顯示在屏幕的位置popup.showAsDropDown(btnPop, 0, 0);// 顯示在某個控件的正下方 }將PopupWindow應用到項目當中
//listview監聽 lvList.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {mCurrentAppInfo = mAdapter.getItem(position);System.out.println(mCurrentAppInfo.name + "被點擊!");showPopupWindow(view);} });-----------------------------------/*** 顯示彈窗*/ private void showPopupWindow(View view) {View contentView = View.inflate(this, R.layout.popup_appinfo, null);TextView tvUninstall = (TextView) contentView.findViewById(R.id.tv_uninstall);TextView tvLaunch = (TextView) contentView.findViewById(R.id.tv_launch);TextView tvShare = (TextView) contentView.findViewById(R.id.tv_share);//設置監聽事件tvUninstall.setOnClickListener(this);tvLaunch.setOnClickListener(this);tvShare.setOnClickListener(this);//初始化PopupWindowPopupWindow popup = new PopupWindow(contentView,LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);popup.setBackgroundDrawable(new ColorDrawable());// 必須設置背景,否則無法返回popup.showAsDropDown(view, 50, -view.getHeight());// 顯示彈窗//漸變動畫AlphaAnimation alpha = new AlphaAnimation(0, 1);alpha.setDuration(500);alpha.setFillAfter(true);//縮放動畫ScaleAnimation scale = new ScaleAnimation(0, 1, 0, 1,Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF,0.5f);scale.setDuration(500);scale.setFillAfter(true);//動畫集合AnimationSet set = new AnimationSet(false);set.addAnimation(alpha);set.addAnimation(scale);//運行動畫contentView.startAnimation(set); }-----------------------------------@Override public void onClick(View v) {if (mCurrentAppInfo == null) {return;}switch (v.getId()) {case R.id.tv_uninstall:System.out.println("卸載" + mCurrentAppInfo.name);break;case R.id.tv_launch:System.out.println("啟動" + mCurrentAppInfo.name);break;case R.id.tv_share:System.out.println("分享" + mCurrentAppInfo.name);break;default:break;} }卸載,啟動和分享的邏輯
/*** 卸載*/ private void uninstall() {if (mCurrentAppInfo.isUserApp) {Intent intent = new Intent();intent.setAction(Intent.ACTION_DELETE);intent.addCategory(Intent.CATEGORY_DEFAULT);intent.setData(Uri.parse("package:" + mCurrentAppInfo.packageName));startActivityForResult(intent, 0);} else {Toast.makeText(this, "無法卸載系統程序!", Toast.LENGTH_SHORT).show();} }@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {// 卸載成功后重新加載應用列表// 此處不必判斷resultCode是否是RESULT_OK, 因為4.1+系統即使卸載成功也始終返回RESULT_CANCELloadAppInfos();super.onActivityResult(requestCode, resultCode, data); }------------------------------------/*** 啟動App*/ private void launchApp() {try {PackageManager pm = this.getPackageManager();Intent intent = pm.getLaunchIntentForPackage(mCurrentAppInfo.packageName);// 獲取應用入口的IntentstartActivity(intent);// 啟動應用} catch (Exception e) {e.printStackTrace();Toast.makeText(this, "無法啟動該應用!", Toast.LENGTH_SHORT).show();} }------------------------------------/*** 分享 此方法會呼起系統中所有支持文本分享的app列表*/ private void shareApp() {Intent intent = new Intent(Intent.ACTION_SEND);intent.addCategory(Intent.CATEGORY_DEFAULT);intent.setType("text/plain");intent.putExtra(Intent.EXTRA_TEXT,"分享給你一個很好的應用哦! 下載地址: https://play.google.com/apps/details?id="+ mCurrentAppInfo.packageName);startActivity(intent); }ListView分類欄常駐效果
//原理: 寫一個TextView常駐在ListView頂欄, 樣式和item中分類欄的樣式完全一樣. 監聽ListView的滑動事件,動態修改TextView的內容//設置listview的滑動監聽 lvList.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {System.out.println("onScroll:" + firstVisibleItem);if (mUserAppList != null && mSystemAppList != null) {if (firstVisibleItem <= mUserAppList.size()) {tvListHead.setText("用戶應用(" + mUserAppList.size() + ")");} else {tvListHead.setText("系統應用(" + mSystemAppList.size()+ ")");}}} });總結
- 上一篇: 基于Apriori算法的网上图书销售ss
- 下一篇: 【小程序源码】看成语猜古诗句好玩解闷小游