第一个Android TV Launcher开源项目

Android TV Launcher开源项目

这个项目是机顶盒桌面,用来播放视频、显示图片、应用管理和其他针对机顶盒产品的设置。目前git和CSDN code上面都没有类似的开源项目,正好本人最近在开发类似产品,网上类似研究还非常少,完全自己实现还是比较困难。所以就把目前的项目开源出来,也有利于其他人少走弯路。
git项目地址
CSDN code项目地址

项目框架结构

技术分享
UI、adapter、实体类、图片cache、网络层。
这个层级关系还是和大部分项目是类似的。

功能模块的详细实现方式

这个部分主要讲一下机顶盒Launcher实现的几个难点,如还有其他问题请留言。

app管理

1.扫描可启动的应用

技术分享
因为原项目效果图不符合开源要求所以去掉了。

    public ArrayList<AppBean> getLaunchAppList() {
        PackageManager localPackageManager = mContext.getPackageManager();
        Intent localIntent = new Intent("android.intent.action.MAIN");
        localIntent.addCategory("android.intent.category.LAUNCHER");
        List<ResolveInfo> localList = localPackageManager.queryIntentActivities(localIntent, 0);
        ArrayList<AppBean> localArrayList = null;
        Iterator<ResolveInfo> localIterator = null;
        if (localList != null) {
            localArrayList = new ArrayList<AppBean>();
            localIterator = localList.iterator();
        }
        while (true) {
            if (!localIterator.hasNext())
                break;
            ResolveInfo localResolveInfo = (ResolveInfo) localIterator.next();
            AppBean localAppBean = new AppBean();
            localAppBean.setIcon(localResolveInfo.activityInfo.loadIcon(localPackageManager));
            localAppBean.setName(localResolveInfo.activityInfo.loadLabel(localPackageManager).toString());
            localAppBean.setPackageName(localResolveInfo.activityInfo.packageName);
            localAppBean.setDataDir(localResolveInfo.activityInfo.applicationInfo.publicSourceDir);
            localAppBean.setLauncherName(localResolveInfo.activityInfo.name);
            String pkgName = localResolveInfo.activityInfo.packageName;
            PackageInfo mPackageInfo;
            try {
                mPackageInfo = mContext.getPackageManager().getPackageInfo(pkgName, 0);
                if ((mPackageInfo.applicationInfo.flags & mPackageInfo.applicationInfo.FLAG_SYSTEM) > 0) {//系统预装
                    localAppBean.setSysApp(true);
                }
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }

            String noSeeApk = localAppBean.getPackageName();

            // 屏蔽一些apk
            if (!noSeeApk.equals("com.cqsmiletv") && !noSeeApk.endsWith("com.starcor.hunan") && !noSeeApk.endsWith("com.tcl.matrix.tventrance")) {
                localArrayList.add(localAppBean);
            }
        }
        return localArrayList;
    }
 //接收安装广播
            if (intent.getAction().equals("android.intent.action.PACKAGE_ADDED")) {

                String packageName = intent.getDataString();
                List<ResolveInfo> list = Tools.findActivitiesForPackage(context, packageName);
                ResolveInfo info = list.get(0);
                PackageManager localPackageManager = context.getPackageManager();
                AppBean localAppBean = new AppBean();
                localAppBean.setIcon(info.activityInfo.loadIcon(localPackageManager));
                localAppBean.setName(info.activityInfo.loadLabel(localPackageManager).toString());
                localAppBean.setPackageName(info.activityInfo.packageName);
                localAppBean.setDataDir(info.activityInfo.applicationInfo.publicSourceDir);

                mAppList.add(localAppBean);
            }

上面的第一个代码模块主要是扫描机顶盒上面安装了哪些应用,把信息填到实体类里面去,还可以根据自己产品要求屏蔽一些apk。
第二个代码块是检测安装广播,根据需要更新数据。

2.扫描可卸载的应用

技术分享

    public ArrayList<AppBean> getUninstallAppList() {
        PackageManager localPackageManager = mContext.getPackageManager();
        Intent localIntent = new Intent("android.intent.action.MAIN");
        localIntent.addCategory("android.intent.category.LAUNCHER");
        List<ResolveInfo> localList = localPackageManager.queryIntentActivities(localIntent, 0);
        ArrayList<AppBean> localArrayList = null;
        Iterator<ResolveInfo> localIterator = null;
        if (localList != null) {
            localArrayList = new ArrayList<AppBean>();
            localIterator = localList.iterator();
        }
        while (true) {
            if (!localIterator.hasNext())
                break;
            ResolveInfo localResolveInfo = (ResolveInfo) localIterator.next();
            AppBean localAppBean = new AppBean();
            localAppBean.setIcon(localResolveInfo.activityInfo.loadIcon(localPackageManager));
            localAppBean.setName(localResolveInfo.activityInfo.loadLabel(localPackageManager).toString());
            localAppBean.setPackageName(localResolveInfo.activityInfo.packageName);
            localAppBean.setDataDir(localResolveInfo.activityInfo.applicationInfo.publicSourceDir);
            String pkgName = localResolveInfo.activityInfo.packageName;
            PackageInfo mPackageInfo;
            try {
                mPackageInfo = mContext.getPackageManager().getPackageInfo(pkgName, 0);
                if ((mPackageInfo.applicationInfo.flags & mPackageInfo.applicationInfo.FLAG_SYSTEM) > 0) {//系统预装
                    localAppBean.setSysApp(true);
                } else {
                    localArrayList.add(localAppBean);
                }
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
        }
        return localArrayList;
    }
//接收卸载广播
            if (intent.getAction().equals("android.intent.action.PACKAGE_REMOVED")) {
                String receiverName = intent.getDataString();
                receiverName = receiverName.substring(8);
                AppBean appBean;
                for(int i=0;i<mAppList.size();i++){
                    appBean = mAppList.get(i);
                   String packageName = appBean.getPackageName();
                    if(packageName.equals(receiverName)){
                        mAppList.remove(i);
                        adapter.notifyDataSetChanged();
                    }
                }
            }

因为系统预装的按常理方法是无法卸载的,应该屏蔽掉。卸载的时候根据广播返回标志更新数据。

3.自启动管理

技术分享

    public ArrayList<AppBean> getAutoRunAppList() {
        PackageManager localPackageManager = mContext.getPackageManager();
        Intent localIntent = new Intent("android.intent.action.MAIN");
        localIntent.addCategory("android.intent.category.LAUNCHER");
        List<ResolveInfo> localList = localPackageManager.queryIntentActivities(localIntent, 0);
        ArrayList<AppBean> localArrayList = null;
        Iterator<ResolveInfo> localIterator = null;
        if (localList != null) {
            localArrayList = new ArrayList<AppBean>();
            localIterator = localList.iterator();
        }

        while (true) {
            if (!localIterator.hasNext())
                break;
            ResolveInfo localResolveInfo = localIterator.next();
            AppBean localAppBean = new AppBean();
            localAppBean.setIcon(localResolveInfo.activityInfo.loadIcon(localPackageManager));
            localAppBean.setName(localResolveInfo.activityInfo.loadLabel(localPackageManager).toString());
            localAppBean.setPackageName(localResolveInfo.activityInfo.packageName);
            localAppBean.setDataDir(localResolveInfo.activityInfo.applicationInfo.publicSourceDir);
            String pkgName = localResolveInfo.activityInfo.packageName;
            String permission = "android.permission.RECEIVE_BOOT_COMPLETED";
            try {
                PackageInfo mPackageInfo = mContext.getPackageManager().getPackageInfo(pkgName, 0);
                if ((PackageManager.PERMISSION_GRANTED == localPackageManager.checkPermission(permission, pkgName)) && !((mPackageInfo.applicationInfo.flags & mPackageInfo.applicationInfo.FLAG_SYSTEM) > 0)) {
                    localArrayList.add(localAppBean);
                }
            } catch (NameNotFoundException e) {
                e.printStackTrace();
            }
        }
        return localArrayList;
    }

扫描应用权限查看是否是自启动app,返回所有自启动app。

    public boolean manageBoot(String pkg,boolean able) {
        Process process = null;
        DataOutputStream dos = null;
        String command = null;
        try {
            process = Runtime.getRuntime().exec("su");
            dos = new DataOutputStream(process.getOutputStream());
            dos.flush();
            command = "export LD_LIBRARY_PATH=/vendor/lib:/system/lib  \n";
            dos.writeBytes(command);
            //(有些cls含有$,需要处理一下,不然会禁止失败,比如微信)
            //但是获取应用是否允许或者禁止开机启动的时候就不用处理cls,否则得不到状态值
           //            cls = cls.replace("$", "\\$");
           //            command = "pm disable " + pkg + "/" + cls + " \n";
            if(able){
                command = "pm enable " + pkg;
            }else{
                command = "pm disable " + pkg;
            }
            dos.writeBytes(command);
            dos.writeBytes("exit " + "\n");
            dos.flush();
            try {
                process.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int exitValue = process.exitValue();
            try {
                if (exitValue == 0) {
                    return true;
                } else {
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
        return false;
    }

禁止应用自启动,需要有root权限。

网络设置

1.WiFi

扫描附近WiFi

技术分享

public void StartScan() {
        mWifiManager.startScan();
        // 得到扫描结果
        mWifiList = mWifiManager.getScanResults();
        // 得到配置好的网络连接
        mWifiConfigurations = mWifiManager.getConfiguredNetworks();
        Log.v("mWifiManager", mWifiManager+"");
        Log.v("mWifiList", mWifiList+"");
        Log.v("mWifiConfigurations", mWifiConfigurations+"");
    }

根据WiFi几种加密方式配置wificonfiguration

public WifiConfiguration CreatConfiguration(String SSID, String Password,
            int Type) {
        WifiConfiguration configuration = new WifiConfiguration();
        configuration.allowedAuthAlgorithms.clear();
        configuration.allowedGroupCiphers.clear();
        configuration.allowedKeyManagement.clear();
        configuration.allowedPairwiseCiphers.clear();
        configuration.allowedProtocols.clear();
        configuration.SSID = "\"" + SSID + "\"";
        WifiConfiguration tempConfiguration = IsExits(SSID, mWifiManager);
        if (tempConfiguration != null) {
            mWifiManager.removeNetwork(tempConfiguration.networkId);
        }
        // WIFICIPHER_NOPASS
        if (Type == 1) {
            configuration.wepKeys[0] = "";
            configuration.allowedKeyManagement
                    .set(WifiConfiguration.KeyMgmt.NONE);
            configuration.wepTxKeyIndex = 0;
        }
        // WIFICIPHER_WEP
        if (Type == 2) {
            configuration.hiddenSSID = true;
            configuration.wepKeys[0] = "\"" + Password + "\"";
            configuration.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.SHARED);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.CCMP);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.TKIP);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.WEP40);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.WEP104);
            configuration.allowedKeyManagement
                    .set(WifiConfiguration.KeyMgmt.NONE);
            configuration.wepTxKeyIndex = 0;
        }
        // WIFICIPHER_WPA
        if (Type == 3) {
            configuration.preSharedKey = "\"" + Password + "\"";
            configuration.hiddenSSID = true;
            configuration.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.OPEN);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.TKIP);
            configuration.allowedKeyManagement
                    .set(WifiConfiguration.KeyMgmt.WPA_PSK);
            configuration.allowedPairwiseCiphers
                    .set(WifiConfiguration.PairwiseCipher.TKIP);
            configuration.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.CCMP);
            configuration.allowedPairwiseCiphers
                    .set(WifiConfiguration.PairwiseCipher.CCMP);
            configuration.status = WifiConfiguration.Status.ENABLED;
        }
        return configuration;
    }

根据上面得到的信息用WiFimanager连接网络,返回的信息可判断是否连接成功。

public int AddNetwork(WifiConfiguration configuration) {
        int configurationId = mWifiManager.addNetwork(configuration);
        boolean b = mWifiManager.enableNetwork(configurationId, true);
        return configurationId;
    }

2.蓝牙

技术分享

在机顶盒中蓝牙常用于蓝牙遥控、键盘、游戏手柄。
注册蓝牙状态广播(连接、正在连接、已连接)

        IntentFilter intent = new IntentFilter();
        // 用BroadcastReceiver来取得搜索结果
        intent.addAction(BluetoothDevice.ACTION_FOUND);
        //每当扫描模式变化的时候,应用程序可以为通过ACTION_SCAN_MODE_CHANGED值来监听全局的消息通知。
        // 比如,当设备停止被搜寻以后,该消息可以被系统通知給应用程序。
        intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        //每当蓝牙模块被打开或者关闭,应用程序可以为通过ACTION_STATE_CHANGED值来监听全局的消息通知。
        intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        intent.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(searchReceiver, intent);

开始搜索

bluetoothAdapter.startDiscovery();

通过广播动态改变蓝牙列表数据

    private BroadcastReceiver searchReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            String action = intent.getAction();
            BluetoothDevice device = null;
            device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if(device!=null){
            }
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                if (device.getBondState() == BluetoothDevice.BOND_NONE) {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("name", device.getName());
                    map.put("type", device.getBluetoothClass().getDeviceClass());
                    map.put("device", device);
                    if (list.indexOf(map) == -1) {// 防止重复添加
                        list.add(map);
                        itemAdapter = new MyBluetoothAdapter(context, list);
                        searchDeviceLV.setAdapter(itemAdapter);
                    }
                }
            } else if (device != null && device.getBondState() == BluetoothDevice.BOND_BONDING) {
                showShortToast("正在配对");
            } else if (device != null && device.getBondState() == BluetoothDevice.BOND_BONDED) {
                pairTVName.setText(device.getName());
                for (int i = 0; i < list.size(); i++) {
                    if (list.get(i).get("device").equals(device)) {
                        pairPosition = i;
                        list.remove(i);
                        itemAdapter.notifyDataSetChanged();
                    }
                }
                showShortToast("配对完成");
            }
        }
    };

连接蓝牙。注:4.4版本以前需用反射方法获取api中隐藏方法

searchDeviceLV.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                BluetoothDevice device = (BluetoothDevice) list.get(position).get("device");
                device.createBond();
                showShortToast("正在配对..");
            }
        });

断开蓝牙,反射方法获取api中隐藏方法。

    static public boolean removeBond(Class btClass,BluetoothDevice btDevice) throws Exception {
        Method removeBondMethod = btClass.getMethod("removeBond");
        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
        return returnValue.booleanValue();
    }

3.网络测速

技术分享

技术分享

机顶盒中网络测速功能是为了查看目前网络能查看的视频质量(超清、高清、标清)。
网络测速的实现主要是从网络下载一个文件,根据每个阶段的数据和时间计算速度。
注:目前网上好多测速的方法都是使用每次读一个字节的方式,这样测速和实际网速有很大误差。本项目采取的是读一行的方式去读,和实际情况比较接近。

public class ReadFileUtil {
    public static byte[] ReadFileFromURL(String URL, NetworkSpeedInfo info) {
        int FileLenth = 0;
        long startTime = 0;
        long intervalTime = 0;
        byte[] b = null;
        URL mUrl = null;
        URLConnection mUrlConnection = null;
        InputStream inputStream = null;
        try {
            mUrl = new URL(URL);
            mUrlConnection = mUrl.openConnection();
            mUrlConnection.setConnectTimeout(15000);
            mUrlConnection.setReadTimeout(15000);
            FileLenth = mUrlConnection.getContentLength();
            inputStream = mUrlConnection.getInputStream();
            NetworkSpeedInfo.totalBytes = FileLenth;
            b = new byte[FileLenth];
            startTime = System.currentTimeMillis();
            BufferedReader bufferReader = new BufferedReader(new InputStreamReader(mUrlConnection.getInputStream()));
            String line;
            byte buffer[];
            while (NetworkSpeedInfo.FILECANREAD&&((line = bufferReader.readLine()) != null)&&FileLenth>NetworkSpeedInfo.FinishBytes) {
                buffer = line.getBytes();
                intervalTime = System.currentTimeMillis() - startTime;
                NetworkSpeedInfo.FinishBytes = NetworkSpeedInfo.FinishBytes + buffer.length;
                if (intervalTime == 0) {
                    NetworkSpeedInfo.Speed = 1000;
                } else {
                    NetworkSpeedInfo.Speed = NetworkSpeedInfo.FinishBytes / intervalTime;
                    double a=(double)NetworkSpeedInfo.FinishBytes/NetworkSpeedInfo.totalBytes*100;
                    NetworkSpeedInfo.progress=(int) a;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return b;
    }
}

如有问题请留言,转载注明出处。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。