第一个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;
}
}
如有问题请留言,转载注明出处。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。