\packages\apps\PackageInstaller

一、一条真实的修改记录

TVOS基于的是一套板卡厂商原有的源码(mstar android8.0版本)原生的这个app安装界面,存在俩个比较严重的用户体验问题,

1’、下面那俩按钮太小了,而且都在右下角干嘛,这么大的屏幕往中间来啊

2、每次安装的时候遥控器的焦点不在安装按钮和完成按钮上,而是在权限列表上,用户还要自己点下去

很好解决,找到布局然后修改布局就可,

install_installing

install_staging

install_failed

install_confirm_perm

install_confirm_perm_update

install_success

install_confirm

然后我就把按钮改大一点,这几个布局都要改哦


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayout android:id="@+id/app_snippet"android:background="?android:attr/colorPrimary"android:layout_width="match_parent"android:layout_height="?android:attr/actionBarSize"android:orientation="horizontal"android:elevation="@dimen/headerElevation"android:gravity="center_vertical"><ImageViewandroid:id="@+id/app_icon"android:layout_width="24dp"android:layout_height="24dp"android:layout_marginStart="16dp"android:scaleType="fitCenter" /><TextViewandroid:id="@+id/app_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="32dp"android:layout_marginEnd="16dp"android:ellipsize="end"android:singleLine="true"android:textAppearance="?android:attr/titleTextStyle" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="center"android:orientation="vertical"android:paddingLeft="16dip"android:paddingRight="16dip"><ImageViewandroid:layout_width="92dp"android:layout_height="92dp"android:layout_marginBottom="12dp"android:contentDescription="@null"android:tint="@color/bigIconColor"android:src="@drawable/ic_file_download" /><ProgressBarandroid:id="@+id/progress_bar"style="?android:attr/progressBarStyleHorizontal"android:layout_width="250dp"android:layout_height="wrap_content"android:indeterminate="false" /><TextViewandroid:id="@+id/center_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_horizontal"android:text="@string/installing"android:textAppearance="?android:attr/textAppearanceMedium" /></LinearLayout><LinearLayoutandroid:id="@+id/buttons_panel"style="?android:attr/buttonBarStyle"android:layout_width="match_parent"android:layout_height="wrap_content"android:measureWithLargestChild="true"android:orientation="horizontal"android:gravity="center"android:padding="8dip"><!-- <Viewandroid:layout_width="0dp"android:layout_height="0dp"android:layout_weight="1" />--><Buttonandroid:id="@+id/cancel_button"style="?android:attr/buttonBarButtonStyle"android:layout_width="320dp"android:layout_height="wrap_content"android:maxLines="2"android:text="@string/cancel" /></LinearLayout></LinearLayout>

\packages\apps\PackageInstaller\src\com\android\packageinstaller\PackageInstallerActivity.java

让他每次进入都获取焦点,完事



二、然后呢!不妨一起来看一下PackageInstaller

调用安装界面

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(xx, "application/vnd.android.package-archive");

没错,就是type--》"application/vnd.android.package-archive"

packages\apps\PackageInstaller\src\com\android\packageinstaller\InstallStart

   Intent nextActivity = new Intent(intent);nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);// The the installation source as the nextActivity thinks this activity is the source, hence// set the originating UID and sourceInfo explicitlynextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {nextActivity.setClass(this, PackageInstallerActivity.class);} else {Uri packageUri = intent.getData();if (packageUri == null) {//uri是空的就退出了// if there's nothing to do, quietly slip into the etherIntent result = new Intent();result.putExtra(Intent.EXTRA_INSTALL_RESULT,PackageManager.INSTALL_FAILED_INVALID_URI);setResult(RESULT_FIRST_USER, result);nextActivity = null;} else {//判断Uri的Scheme,如果是就跳转到InstallStagingif (packageUri.getScheme().equals(SCHEME_CONTENT)) {nextActivity.setClass(this, InstallStaging.class);} else {nextActivity.setClass(this, PackageInstallerActivity.class);}}}if (nextActivity != null) {startActivity(nextActivity);}finish();

跳转根据uri的类型跳转吧,InstallStaging与PackageInstallerActivity

7.0及以后的系统,file://ur就会报FileUriException异常,所以需要使用FileContentProvider来将file://uri替换成content://uri,

packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))就是这样的一个判断条件,7.0以上走InstallStaging。

看看InstallStaging ,来看下onresume

 @Overrideprotected void onResume() {super.onResume();// This is the first onResume in a single life of the activityif (mStagingTask == null) {// File does not exist, or became invalidif (mStagedFile == null) {// Create file delayed to be able to show errortry {mStagedFile = TemporaryFileManager.getStagedFile(this);} catch (IOException e) {showError();return;}}mStagingTask = new StagingAsyncTask();mStagingTask.execute(getIntent().getData());}}

哦吼,你会发现起了个stagintask,把uri直接传进去了

private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {@Overrideprotected Boolean doInBackground(Uri... params) {//取,存-----缓存安装包文件----到mStagedFileif (params == null || params.length <= 0) {return false;}Uri packageUri = params[0];try (InputStream in = getContentResolver().openInputStream(packageUri)) {// Despite the comments in ContentResolver#openInputStream the returned stream can// be null.if (in == null) {return false;}try (OutputStream out = new FileOutputStream(mStagedFile)) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = in.read(buffer)) >= 0) {// Be nice and respond to a cancellationif (isCancelled()) {return false;}out.write(buffer, 0, bytesRead);}}} catch (IOException | SecurityException e) {Log.w(LOG_TAG, "Error staging apk from content URI", e);return false;}return true;}@Overrideprotected void onPostExecute(Boolean success) {if (success) {// Now start the installation again from a file
//哦吼,又跳转了!!!!!!!还是PackageInstallerActivityIntent installIntent = new Intent(getIntent());installIntent.setClass(InstallStaging.this, PackageInstallerActivity.class);installIntent.setData(Uri.fromFile(mStagedFile));installIntent.setFlags(installIntent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);startActivityForResult(installIntent, 0);} else {showError();}}}

将content://uri 转成了file://uri,又跳转了PackageInstallerActivity

PackageInstallerActivity oncrate

  bindUi(R.layout.install_confirm, false);checkIfAllowedAndInitiateInstall();

checkIfAllowedAndInitiateInstall检查是不是已经允许了安装位置源应用

然后initiateInstall

然后startInstallConfirm

  private void startInstallConfirm() {// We might need to show permissions, load layout with permissionsif (mAppInfo != null) {//加载app更新布局bindUi(R.layout.install_confirm_perm_update, true);} else {//加载确认安装布局bindUi(R.layout.install_confirm_perm, true);}((TextView) findViewById(R.id.install_confirm_question)).setText(R.string.install_confirm_question);TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);tabHost.setup();ViewPager viewPager = (ViewPager)findViewById(R.id.pager);TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);// If the app supports runtime permissions the new permissions will// be requested at runtime, hence we do not show them at install.boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion>= Build.VERSION_CODES.M;boolean permVisible = false;mScrollView = null;mOkCanInstall = false;int msg = 0;//加载需要的权限,显示到布局中mScrollViewAppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);if (mAppInfo != null) {msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? R.string.install_confirm_question_update_system: R.string.install_confirm_question_update;mScrollView = new CaffeinatedScrollView(this);mScrollView.setFillViewport(true);boolean newPermissionsFound = false;if (!supportsRuntimePermissions) {newPermissionsFound =(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);if (newPermissionsFound) {permVisible = true;mScrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_NEW));}}if (!supportsRuntimePermissions && !newPermissionsFound) {LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);TextView label = (TextView)inflater.inflate(R.layout.label, null);label.setText(R.string.no_new_perms);mScrollView.addView(label);}adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(getText(R.string.newPerms)), mScrollView);}if (!supportsRuntimePermissions && N > 0) {permVisible = true;LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);View root = inflater.inflate(R.layout.permissions_list, null);if (mScrollView == null) {mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);}((ViewGroup)root.findViewById(R.id.permission_list)).addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(getText(R.string.allPerms)), root);}if (!permVisible) {if (mAppInfo != null) {// This is an update to an application, but there are no// permissions at all.msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0? R.string.install_confirm_question_update_system_no_perms: R.string.install_confirm_question_update_no_perms;} else {// This is a new application with no permissions.msg = R.string.install_confirm_question_no_perms;}// We do not need to show any permissions, load layout without permissionsbindUi(R.layout.install_confirm, true);mScrollView = null;}if (msg != 0) {((TextView)findViewById(R.id.install_confirm_question)).setText(msg);}if (mScrollView == null) {// There is nothing to scroll view, so the ok button is immediately// set to install.mOk.setText(R.string.install);mOkCanInstall = true;} else {mScrollView.setFullScrollAction(new Runnable() {@Overridepublic void run() {mOk.setText(R.string.install);mOkCanInstall = true;}});}}
public void onClick(View v) {if (v == mOk) {//ok被点击,开始安装if (mOk.isEnabled()) {if (mOkCanInstall || mScrollView == null) {if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, true);finish();} else {startInstall();}} else {mScrollView.pageScroll(View.FOCUS_DOWN);}}} else if (v == mCancel) {// Cancel and finishsetResult(RESULT_CANCELED);if (mSessionId != -1) {mInstaller.setPermissionsResult(mSessionId, false);}finish();}}

startInstall();跳转到InstallInstalling

InstallInstalling    onresunme启动InstallingAsyncTask

InstallingAsyncTask的doInBackground方法中会根据包的Uri,将APK的信息通过IO流的形式写入到PackageInstaller.Session中

  /*** Send the package to the package installer and then register a event result observer that* will call {@link #launchFinishBasedOnResult(int, int, String)}*/private final class InstallingAsyncTask extends AsyncTask<Void, Void,PackageInstaller.Session> {volatile boolean isDone;@Overrideprotected PackageInstaller.Session doInBackground(Void... params) {PackageInstaller.Session session;try {session = getPackageManager().getPackageInstaller().openSession(mSessionId);} catch (IOException e) {return null;}session.setStagingProgress(0);try {File file = new File(mPackageURI.getPath());try (InputStream in = new FileInputStream(file)) {long sizeBytes = file.length();try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {byte[] buffer = new byte[4096];while (true) {int numRead = in.read(buffer);if (numRead == -1) {session.fsync(out);break;}if (isCancelled()) {session.close();break;}out.write(buffer, 0, numRead);if (sizeBytes > 0) {float fraction = ((float) numRead / (float) sizeBytes);session.addProgress(fraction);}}}}return session;} catch (IOException | SecurityException e) {Log.e(LOG_TAG, "Could not write package", e);session.close();return null;} finally {synchronized (this) {isDone = true;notifyAll();}}}@Overrideprotected void onPostExecute(PackageInstaller.Session session) {if (session != null) {Intent broadcastIntent = new Intent(BROADCAST_ACTION);broadcastIntent.setPackage(getPackageManager().getPermissionControllerPackageName());broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);PendingIntent pendingIntent = PendingIntent.getBroadcast(InstallInstalling.this,mInstallId,broadcastIntent,PendingIntent.FLAG_UPDATE_CURRENT);session.commit(pendingIntent.getIntentSender());mCancelButton.setEnabled(false);setFinishOnTouchOutside(false);} else {getPackageManager().getPackageInstaller().abandonSession(mSessionId);if (!isCancelled()) {launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);}}}}

这里出现了一个   session = getPackageManager().getPackageInstaller().openSession(mSessionId);调用PackageInstaller.Session的commit方法,将APK的信息交由PMS处理。

也就是说安装的逻辑就跑到PM那里去咯  frameworks/base/core/java/android/content/pm/PackageInstaller.java

然后呢结果通过InstallEventReceiver监听安装结果,这样基本上就完成了。所以PackageInstaller想要知道是如何安装的,还要去看pm。

PackageInstaller (tv 修改安装app界面按钮及自动获取焦点)附源码分析相关推荐

  1. 用树型模型管理App数字和红点提示(附源码Demo)

    我们平常接触到的大部分App,在收到新消息的时候一般都会以数字或红点的形式提示出来.比如在微信当中,当某位好友给我们发来新的聊天消息的时候,在相应的会话上就会有一个数字来表示未读消息的数目:再比如当微 ...

  2. uniapp支付之APP微信支付unicloud版(附源码)

    1 先上效果图 为啥要用 unicloud ? 不用搭建自己的服务器,不用买域名,不用备案域名,不用支持https.只需要一个简单的云函数,就可以轻松的实现微信支付功能 问:服务端语言是什么,有框架限 ...

  3. 8种CSS3按钮动画特效【附源码】

    这8款CSS3按钮动画特效.在该特效中,提供了8种按钮动画效果.每种动画在鼠标悬停到按钮上面的时候,都会触发按钮动画. 动画效果截图: 使用方法 HTML结构 最简单的按钮HTML结构如下. < ...

  4. android分贝仪界面,android声音检测仪---分贝仪 (附源码)

    android声音检测仪---分贝仪 文章出处:大黑个人博客--android声音检测仪---分贝仪 源码下载地址:https://github.com/halibobo/SoundMeter 背景 ...

  5. 高仿小米计算器界面UI 适合新手学习 [附源码]

    初学Android尝试着做一些布局,看到手机上的小米计算器界面简洁,适合新手尝试,于是做了一下,但是未实现逻辑,只是高仿界面. 小米计算器UI 高仿小米计算器UI 源码:https://downloa ...

  6. 【CSS按钮特效】css如何实现科技感好看按钮效果(尾附源码下载)

    [写在前面]这两天还是比较痴迷于CSS特效的,甚至还想着去用CSS做动画片呢,希望后面能做到,今天主要讲的是我们页面常见的元素-按钮,很多时候按钮也需要高级化,但是很多人苦于没有途径去寻找,于是乎借这 ...

  7. springboot+基于vue的响应式代购商城APP的设计与实现 毕业设计-附源码191654

    Springboot响应式代购商城APP 摘 要 近年来,随着移动互联网的快速发展,电子商务越来越受到网民们的欢迎,电子商务对国家经济的发展也起着越来越重要的作用.简单的流程.便捷可靠的支付方式.快捷 ...

  8. (基于安卓app开发毕业设计)上课考勤管理(附源码+论文)

    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦.

  9. 仿酷狗播放显示界面返回动画效果(附源码)

    分类 奇技淫巧 的第一篇博文,以后会有更多奇技淫巧与大家分享~ 先看看酷狗的效果: [img]http://dl2.iteye.com/upload/attachment/0096/6694/1e91 ...

  10. 航空机票APP的设计与实现【AS开发+SQLITE】【附源码】

    航空机票APP的设计与实现[附源码] 演示 学长带你飞之 航空机票小app 1 背景及意义 在信息技术和网络技术不断发展的当前社会,航空公司赖以生存的根本是提升自己的服务,只有以客户为本,企业才能保证 ...

最新文章

  1. 2022-2028年中国毛纺行业研究及前瞻分析报告
  2. c语言中存储字符用什么函数,那些C语言中你不知道的字符串函数(坑)
  3. 《2019中国硬科技发展白皮书》发布,中美硬科技创新指数PK
  4. python变量初始化_tensorflow之变量初始化(tf.Variable)使用详解
  5. UESTC_秋实大哥与花 2015 UESTC Training for Data StructuresProblem B
  6. JAVA中自增自减运算符(i++与++i的区别)
  7. android ndk opencv 3,opencv_and_opencv_contrib
  8. TP框架的目录结构总结
  9. Ubuntu上安装NS3(最详细的图文介绍)
  10. 8、Java中XML表示衣服尺码信息的文档编写
  11. org.apache.felix.http.jetty %bundles.pluginTitle: Cannot start (org.osgi.framework.BundleException:
  12. HashMap 容量为2次幂的原因
  13. maya中英文对照_Maya菜单中英文对照(全)
  14. 基于jsp+mysql+ssm妇女联合会管理系统-计算机毕业设计
  15. Android平板做win10显示器,win10怎么投影到安卓平板上
  16. python爬虫防屏蔽_Python爬虫防封杀方法集合
  17. 忽然想到了,国内物价上涨是不是和贸易顺差太大有关
  18. (转)C#中Abstract和Virtual
  19. 浅谈项目管理之平衡原则
  20. VM修改BIOS安装OEM系统

热门文章

  1. 西门子阀门定位器安装教程来啦,不会安装的宝贝们仔细看看咯!
  2. 京东/拼多多淘客小程序跳入路径
  3. adb发送什么命令能在手机屏幕弹窗显示_如何通过命令给手机刷机
  4. Android修炼之道—布局优化( 100 喵喵币)
  5. xp系统怎么添加ntp服务器,1 Windows xp NTP服务器的配置
  6. ENVI Landsat8影像掩膜裁剪
  7. 运输计划 洛谷P2680
  8. jq ui.dialog.js简介
  9. 【PS】抠图教程(0基础快速入门)
  10. VBA按行读取TXT文本文件