Android 高仿豌豆莢 一鍵安裝app 功能 實現


以往我們那些應用市場 幫我們安裝app的時候  我們都得點確定,當然你如果 root 以后 不用點確定 也能自動安裝了,后來豌豆莢 推出了一個功能 非root的手機也能不點確定 直接幫你安裝好。(如果不理解我這段話意思的同學 趕緊試用豌豆莢就知道了)

實際上 這個功能還是蠻重要的,比如我們的app 如果需要強制升級 什么的,用戶下載好 你啟動installer 然后還要用戶點確定才能安裝,你看這就是用戶體驗不好嗎 對吧,學會這個可以幫我們做很多事。

當然了 首先要感謝豌豆莢團隊 在csdn做的采訪,這是這篇文章的基礎 http://www.csdn.net/article/1970-01-01/2824737 他透露了這個功能點的point。

好廢話不多說 我們直接上代碼吧,因為這個功能所涉及到的api 比較小眾,我就不過多介紹了,有需要的同學可以參考 官方文檔的這個training http://developer.android.com/intl/zh-cn/training/accessibility/service.html

我着重提一下,千萬不要用這種方式去實現 流氓軟件的流氓功能,作為android 開發,一個好的生態圈是要我們自己去維護的,不要學 百度 那種流氓apk!

首先 我們來定義一個特殊的服務:

 1 package com.example.administrator.powertest;
 2 
 3 import android.accessibilityservice.AccessibilityService;
 4 import android.view.accessibility.AccessibilityEvent;
 5 import android.view.accessibility.AccessibilityNodeInfo;
 6 
 7 import java.util.List;
 8 
 9 /**
10  * 這個服務是不需要你在activity里去開啟的,屬於系統級別輔助服務 需要在設置里去手動開啟 和我們平常app里
11  * 經常使用的service 是有很大不同的 非常特殊
12  * 你可以在 \sdk\samples\android-23\legacy\ApiDemos 這樣的目錄下 找到這個工程 這個工程下面有一個accessibility
13  * 包 里面有關於這個服務的demo 當然他們那個demo 非常復雜,但是信息量很大,有興趣深入研究的同學可以多看demo
14  * 我這里只實現最基本的功能 且沒有做冗余和異常處理,只包含基礎功能,不能作為實際業務上線!
15  */
16 public class MyAccessibilityService extends AccessibilityService {
17     public MyAccessibilityService() {
18     }
19 
20     /**
21      * AccessibilityService 這個服務可以關聯很多屬性,這些屬性 一般可以通過代碼在這個方法里進行設置,
22      * 我這里偷懶 把這些設置屬性的流程用xml 寫好 放在manifest里,如果你們要使用的時候需要區分版本號
23      * 做兼容,在老的版本里是無法通過xml進行引用的 只能在這個方法里手寫那些屬性 一定要注意.
24      * 同時你的業務如果很復雜比如需要初始化廣播啊之類的工作 都可以在這個方法里寫。
25      */
26     @Override
27     protected void onServiceConnected() {
28         super.onServiceConnected();
29     }
30 
31     /**
32      * 當你這個服務正常開啟的時候,就可以監聽事件了,當然監聽什么事件,監聽到什么程度 都是由給這個服務的屬性來決定的,
33      * 我的那些屬性寫在xml里了。
34      */
35     @Override
36     public void onAccessibilityEvent(AccessibilityEvent event) {
37         /**
38          * 事件是分很多種的,我這里是最簡單的那種,只演示核心功能,如果要做成業務上線 這里推薦一個方法可以快速理解這里的type屬性。
39          * 把這個type的int 值取出來 並轉成16進制,然后去AccessibilityEvent 源碼里find。順便看注釋 ,這樣是迅速理解type類型的方法
40          */
41         final int eventType = event.getEventType();
42         switch (eventType) {
43             case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
44                 //這個地方沒什么好說的 你就理解成 找到當前界面 包含有安裝 這個關鍵詞的 所有節點就可以了。返回這些節點的list
45                 //注意這里的find 其實是contains的意思,比如你界面上有2個節點,一個節點內容是安裝1 一個節點內容是安裝2,那這2個節點是都會返回過來的
46                 //除了有根據Text找節點的方法 還有根據Id找節點的方法。考慮到眾多手機rom都不一樣,這里需要大家多測試一下,有的rom packageInstall
47                 //定制的比較深入,可能和官方rom里差的很遠 這里就要做冗余處理,可以告訴大家一個小技巧 你就把這些rom的 安裝器打開 然后
48                 //通過ddms里 看view結構的按鈕 直接進去看就行了,可以直接看到那個界面屬於哪個包名,也可以看到你要捕獲的那個按鈕的id是什么 很方便!
49                 List<AccessibilityNodeInfo> list = event.getSource().findAccessibilityNodeInfosByText("安裝");
50                 if (null!=list){
51                     for (AccessibilityNodeInfo info : list) {
52                         if (info.getText().toString().equals("安裝"))
53                         {
54                             //找到你的節點以后 就直接點擊他就行了
55                             info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
56                         }
57                     }
58                 }
59                 break;
60             default:
61                 break;
62         }
63     }
64     @Override
65     public void onInterrupt() {
66 
67     }
68 }

服務定義好了 就要在配置文件里配置一下,看manifest的主要代碼:

 1 <!-- label 這個就是在設置界面顯示的label 應該比較好理解了-->
 2         <service
 3             android:name=".MyAccessibilityService"
 4             android:exported="true"
 5             android:label="女神的自動裝"
 6             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
 7             <intent-filter>
 8                 <action android:name="android.accessibilityservice.AccessibilityService" />
 9             </intent-filter>
10             <meta-data
11                 android:name="android.accessibilityservice"
12                 android:resource="@xml/taskbackconfig" />
13         </service>

然后我們在res路徑下 新建一個xml 文件夾 並在下面 新建一個xml文件取名為taskbackconfig.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--
 3   Copyright (C) 2011 The Android Open Source Project
 4 
 5   Licensed under the Apache License, Version 2.0 (the "License");
 6   you may not use this file except in compliance with the License.
 7   You may obtain a copy of the License at
 8 
 9        http://www.apache.org/licenses/LICENSE-2.0
10 
11   Unless required by applicable law or agreed to in writing, software
12   distributed under the License is distributed on an "AS IS" BASIS,
13   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   See the License for the specific language governing permissions and
15   limitations under the License.
16  -->
17 <!--
18 這個就是給我們的AccessibilityService 設置屬性的,當然你也可以在代碼里 的 connnect函數里 手動設置。可以向下兼容。
19 accessibilityFeedbackType 這個屬性如果不設置的話 我們那個onAccessibilityEvent 這個回調函數 根本回調不了 所以這里要注意
20 packageNames 這個屬性 就是捕獲什么app的行為的,比如我這里寫的包名是packageinstaller 那就肯定只能捕獲安裝器的 事件了
21 有的rom 安裝器可能不是這個包名 那你就要進行特殊設置了,此外這個屬性你如果什么都不寫 就意味着 你可以捕獲所有手機的動作
22 如果你要做流氓軟件的話 可以packageNames 里面什么都不寫。。。甚至可以操作支付寶 給你打錢。。。如果你知道用戶密碼的話。
23 當然你如果真這么做了 相信捕獲一次用戶輸入密碼的行為 也是很容易的。。細思極恐 我就不往下深入了。。。
24 
25 description 這個就是對你那個申請服務的時候說明了,可以寫的煽情一點 讓用戶打開這個服務的可能性更高一點。。。
26 -->
27 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
28     android:accessibilityEventTypes="typeAllMask"
29     android:notificationTimeout="100"
30     android:packageNames="com.android.packageinstaller"
31     android:accessibilityFeedbackType="feedbackSpoken"
32     android:canRetrieveWindowContent="true"
33     android:description="@string/hint" />

到此時就差不多了,我們再把activity的代碼放上來:

  1 package com.example.administrator.powertest;
  2 
  3 import android.content.BroadcastReceiver;
  4 import android.content.Context;
  5 import android.content.Intent;
  6 import android.content.IntentFilter;
  7 import android.net.Uri;
  8 import android.os.Bundle;
  9 import android.provider.Settings;
 10 import android.support.design.widget.FloatingActionButton;
 11 import android.support.design.widget.Snackbar;
 12 import android.support.v4.content.LocalBroadcastManager;
 13 import android.support.v7.app.AppCompatActivity;
 14 import android.support.v7.widget.Toolbar;
 15 import android.util.Log;
 16 import android.view.View;
 17 import android.view.Menu;
 18 import android.view.MenuItem;
 19 import android.widget.TextView;
 20 
 21 import java.io.File;
 22 
 23 public class MainActivity extends AppCompatActivity {
 24 
 25     private TextView tv,installTv;
 26     /**
 27      * 你得引導用戶去設置界面嗎,你不能讓用戶自己去找吧。
 28      */
 29     private static final Intent sSettingsIntent =
 30             new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
 31     //這里就假設想要安裝的apk 是扇貝網 並且在sd卡根目錄下面
 32     private static final String FILE_PATH="/mnt/sdcard/shanbeidanci6.0.000.apk";
 33 
 34 
 35     @Override
 36     protected void onCreate(Bundle savedInstanceState) {
 37         super.onCreate(savedInstanceState);
 38         setContentView(R.layout.activity_main);
 39         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 40         setSupportActionBar(toolbar);
 41 
 42         FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
 43         fab.setOnClickListener(new View.OnClickListener() {
 44             @Override
 45             public void onClick(View view) {
 46                 Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
 47                         .setAction("Action", null).show();
 48             }
 49         });
 50 
 51         tv = (TextView) findViewById(R.id.tv);
 52         tv.setOnClickListener(new View.OnClickListener() {
 53 
 54             @Override
 55             public void onClick(View v) {
 56                 startActivity(sSettingsIntent);
 57             }
 58         });
 59         installTv=(TextView)this.findViewById(R.id.tv2);
 60         installTv.setOnClickListener(new View.OnClickListener(){
 61 
 62             @Override
 63             public void onClick(View v) {
 64                 //調用安裝器去安裝我們的apk 一鍵安裝開始啦,如果用戶把那個服務打開了的話。
 65                 Intent intent = new Intent(Intent.ACTION_VIEW);
 66                 intent.setDataAndType(Uri.fromFile(new File(FILE_PATH)), "application/vnd.android.package-archive");
 67                 startActivity(intent);
 68             }
 69         });
 70     }
 71 
 72     @Override
 73     public boolean onCreateOptionsMenu(Menu menu) {
 74         // Inflate the menu; this adds items to the action bar if it is present.
 75         getMenuInflater().inflate(R.menu.menu_main, menu);
 76         return true;
 77     }
 78 
 79     private class ResponseReceiver extends BroadcastReceiver {
 80 
 81         public void onReceive(Context context, Intent intent) {
 82 
 83             tv.setText(intent.getStringExtra("msg"));
 84         }
 85     }
 86 
 87     @Override
 88     public boolean onOptionsItemSelected(MenuItem item) {
 89         // Handle action bar item clicks here. The action bar will
 90         // automatically handle clicks on the Home/Up button, so long
 91         // as you specify a parent activity in AndroidManifest.xml.
 92         int id = item.getItemId();
 93 
 94         //noinspection SimplifiableIfStatement
 95         if (id == R.id.action_settings) {
 96             return true;
 97         }
 98 
 99         return super.onOptionsItemSelected(item);
100     }
101 }

到此所有代碼就結束了,如果你想做的好一點 ,請自己做冗余異常處理,我這里主要演示功能就不做的那么細致了,最后看下跑起來的效果吧:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM