Add a demo of the Session-based PackageInstaller to ApiDemos.

Test: Manually.
Change-Id: If084477e8970777ef5cd90c55afe86a116ee1458
This commit is contained in:
Peter Visontay
2017-11-16 19:58:57 +00:00
parent 656dd61384
commit 75bca25be1
6 changed files with 198 additions and 4 deletions

View File

@@ -39,7 +39,8 @@
<uses-permission android:name="android.permission.TRANSMIT_IR" /> <uses-permission android:name="android.permission.TRANSMIT_IR" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<!-- For android.media.audiofx.Visualizer --> <!-- For android.media.audiofx.Visualizer -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
@@ -1310,6 +1311,19 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".content.InstallApkSessionApi"
android:label="@string/activity_install_apk_session_api"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
<intent-filter>
<action android:exported="true"
android:name="com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED" />
</intent-filter>
</activity>
<activity android:name=".content.DocumentsSample" <activity android:name=".content.DocumentsSample"
android:label="@string/activity_documents" android:label="@string/activity_documents"
android:enabled="@bool/atLeastKitKat"> android:enabled="@bool/atLeastKitKat">

View File

@@ -14,8 +14,8 @@
limitations under the License. limitations under the License.
--> -->
<!-- Demonstrates starting and stopping a local service.
See corresponding Java code com.android.sdk.app.LocalSerice.java. --> <!-- Demonstrates installing and uninstalling packages. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal" android:gravity="center_horizontal"

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Demonstrates installing and uninstalling packages using the Session API. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<TextView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Demonstrates using the PackageInstaller's Session API to install an application."/>
<Button android:id="@+id/install"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Install">
</Button>
</LinearLayout>

View File

@@ -492,6 +492,7 @@
<string name="activity_media_content_observer">Content/Provider/Media Content Observer</string> <string name="activity_media_content_observer">Content/Provider/Media Content Observer</string>
<string name="activity_install_apk">Content/Packages/Install Apk</string> <string name="activity_install_apk">Content/Packages/Install Apk</string>
<string name="activity_install_apk_session_api">Content/Packages/Install Apk using Session API</string>
<string name="ir_send">Send IR</string> <string name="ir_send">Send IR</string>
<string name="ir_get_freqs">Get Carrier Frequencies</string> <string name="ir_get_freqs">Get Carrier Frequencies</string>

View File

@@ -46,7 +46,10 @@ import java.io.InputStream;
/** /**
* Demonstration of styled text resources. * Demonstration of package installation and uninstallation using the original (non-Session)
* package installation API that uses {@link Intent#ACTION_INSTALL_PACKAGE}.
*
* @see InstallApkSessionApi for a demo of the newer Session API.
*/ */
public class InstallApk extends Activity { public class InstallApk extends Activity {
static final int REQUEST_INSTALL = 1; static final int REQUEST_INSTALL = 1;

View File

@@ -0,0 +1,141 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.apis.content;
// Need the following import to get access to the app resources, since this
// class is in a sub-package.
import com.example.android.apis.R;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageInstaller;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Demonstration of package installation and uninstallation using the package installer Session
* API.
*
* @see InstallApk for a demo of the original (non-Session) API.
*/
public class InstallApkSessionApi extends Activity {
private static final String PACKAGE_INSTALLED_ACTION =
"com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.install_apk_session_api);
// Watch for button clicks.
Button button = (Button) findViewById(R.id.install);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
PackageInstaller.Session session = null;
try {
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
addApkToInstallSession("HelloActivity.apk", session);
// Create an install status receiver.
Context context = InstallApkSessionApi.this;
Intent intent = new Intent(context, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);
} catch (IOException e) {
throw new RuntimeException("Couldn't install package", e);
} catch (RuntimeException e) {
if (session != null) {
session.abandon();
}
throw e;
}
}
});
}
private void addApkToInstallSession(String assetName, PackageInstaller.Session session)
throws IOException {
// It's recommended to pass the file size to openWrite(). Otherwise installation may fail
// if the disk is almost full.
try (OutputStream packageInSession = session.openWrite("package", 0, -1);
InputStream is = getAssets().open(assetName)) {
byte[] buffer = new byte[16384];
int n;
while ((n = is.read(buffer)) >= 0) {
packageInSession.write(buffer, 0, n);
}
}
}
// Note: this Activity must run in singleTop launchMode for it to be able to receive the intent
// in onNewIntent().
@Override
protected void onNewIntent(Intent intent) {
Bundle extras = intent.getExtras();
if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
int status = extras.getInt(PackageInstaller.EXTRA_STATUS);
String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
switch (status) {
case PackageInstaller.STATUS_PENDING_USER_ACTION:
// This test app isn't privileged, so the user has to confirm the install.
Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
startActivity(confirmIntent);
break;
case PackageInstaller.STATUS_SUCCESS:
Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show();
break;
case PackageInstaller.STATUS_FAILURE:
case PackageInstaller.STATUS_FAILURE_ABORTED:
case PackageInstaller.STATUS_FAILURE_BLOCKED:
case PackageInstaller.STATUS_FAILURE_CONFLICT:
case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
case PackageInstaller.STATUS_FAILURE_INVALID:
case PackageInstaller.STATUS_FAILURE_STORAGE:
Toast.makeText(this, "Install failed! " + status + ", " + message,
Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(this, "Unrecognized status received from installer: " + status,
Toast.LENGTH_SHORT).show();
}
}
}
}