Merge "Add manual rollback sample app"

This commit is contained in:
Gavin Corkery
2022-06-24 10:19:34 +00:00
committed by Gerrit Code Review
6 changed files with 320 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
// Copyright (C) 2022 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_app {
name: "SampleRollbackApp",
srcs: [
"src/**/*.java",
],
resource_dirs: ["res"],
certificate: "platform",
sdk_version: "system_current",
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.sample.rollbackapp" >
<uses-permission android:name="android.permission.TEST_MANAGE_ROLLBACKS" />
<application
android:label="@string/title_activity_main">
<activity
android:name="com.android.sample.rollbackapp.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/trigger_rollback_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
style="?android:attr/buttonBarButtonStyle"
android:text="Rollback Selected" />
<ListView
android:id="@+id/listView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="?android:attr/dividerHorizontal"
android:dividerHeight="1dp" />
</LinearLayout>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<TextView android:id="@+id/rollback_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"/>
<TextView android:id="@+id/rollback_packages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16dp"/>
<CheckBox android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Roll Back"/>
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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.
-->
<resources>
<string name="title_activity_main" description="Launcher title">Rollback Sample App</string>
</resources>

View File

@@ -0,0 +1,159 @@
/*
* Copyright (C) 2022 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.android.sample.rollbackapp;
import static android.app.PendingIntent.FLAG_MUTABLE;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MainActivity extends Activity {
List<Integer> mIdsToRollback = new ArrayList<>();
Button mTriggerRollbackButton;
RollbackManager mRollbackManager;
static final String ROLLBACK_ID_EXTRA = "rollbackId";
static final String ACTION_NAME = MainActivity.class.getName();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView rollbackListView = findViewById(R.id.listView);
mRollbackManager = getApplicationContext().getSystemService(RollbackManager.class);
initTriggerRollbackButton();
// Populate list of available rollbacks.
List<RollbackInfo> availableRollbacks = mRollbackManager.getAvailableRollbacks();
CustomAdapter adapter = new CustomAdapter(availableRollbacks);
rollbackListView.setAdapter(adapter);
// Register receiver for rollback status events.
getApplicationContext().registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context,
Intent intent) {
int rollbackId = intent.getIntExtra(ROLLBACK_ID_EXTRA, -1);
int rollbackStatusCode = intent.getIntExtra(RollbackManager.EXTRA_STATUS,
RollbackManager.STATUS_FAILURE);
String rollbackStatus = "FAILED";
if (rollbackStatusCode == RollbackManager.STATUS_SUCCESS) {
rollbackStatus = "SUCCESS";
}
makeToast("Status for rollback ID " + rollbackId + " is " + rollbackStatus);
}}, new IntentFilter(ACTION_NAME), Context.RECEIVER_NOT_EXPORTED);
}
private void initTriggerRollbackButton() {
mTriggerRollbackButton = findViewById(R.id.trigger_rollback_button);
mTriggerRollbackButton.setClickable(false);
mTriggerRollbackButton.setOnClickListener(v -> {
// Commits all selected rollbacks. Rollback status events will be sent to our receiver.
for (int i = 0; i < mIdsToRollback.size(); i++) {
Intent intent = new Intent(ACTION_NAME);
intent.putExtra(ROLLBACK_ID_EXTRA, mIdsToRollback.get(i));
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), 0, intent, FLAG_MUTABLE);
mRollbackManager.commitRollback(mIdsToRollback.get(i),
Collections.emptyList(),
pendingIntent.getIntentSender());
}
});
}
private void makeToast(String message) {
runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show());
}
public class CustomAdapter extends BaseAdapter {
List<RollbackInfo> mRollbackInfos;
LayoutInflater mInflater = LayoutInflater.from(getApplicationContext());
CustomAdapter(List<RollbackInfo> rollbackInfos) {
mRollbackInfos = rollbackInfos;
}
@Override
public int getCount() {
return mRollbackInfos.size();
}
@Override
public Object getItem(int position) {
return mRollbackInfos.get(position);
}
@Override
public long getItemId(int position) {
return mRollbackInfos.get(position).getRollbackId();
}
@Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = mInflater.inflate(R.layout.listitem_rollbackinfo, null);
}
RollbackInfo rollbackInfo = mRollbackInfos.get(position);
TextView rollbackIdView = view.findViewById(R.id.rollback_id);
rollbackIdView.setText("Rollback ID " + rollbackInfo.getRollbackId());
TextView rollbackPackagesTextView = view.findViewById(R.id.rollback_packages);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < rollbackInfo.getPackages().size(); i++) {
PackageRollbackInfo pkgInfo = rollbackInfo.getPackages().get(i);
sb.append(pkgInfo.getPackageName() + ": "
+ pkgInfo.getVersionRolledBackFrom().getLongVersionCode() + " -> "
+ pkgInfo.getVersionRolledBackTo().getLongVersionCode() + ",");
}
sb.deleteCharAt(sb.length() - 1);
rollbackPackagesTextView.setText(sb.toString());
CheckBox checkbox = view.findViewById(R.id.checkbox);
checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
mIdsToRollback.add(rollbackInfo.getRollbackId());
} else {
mIdsToRollback.remove(Integer.valueOf(rollbackInfo.getRollbackId()));
}
mTriggerRollbackButton.setClickable(mIdsToRollback.size() > 0);
});
return view;
}
}
}