1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.sample.rollbackapp; 18 19 import static android.app.PendingIntent.FLAG_MUTABLE; 20 21 import android.app.Activity; 22 import android.app.PendingIntent; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.rollback.PackageRollbackInfo; 28 import android.content.rollback.RollbackInfo; 29 import android.content.rollback.RollbackManager; 30 import android.os.Bundle; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.BaseAdapter; 35 import android.widget.Button; 36 import android.widget.CheckBox; 37 import android.widget.ListView; 38 import android.widget.TextView; 39 import android.widget.Toast; 40 41 import java.util.ArrayList; 42 import java.util.Collections; 43 import java.util.List; 44 45 public class MainActivity extends Activity { 46 47 List<Integer> mIdsToRollback = new ArrayList<>(); 48 Button mTriggerRollbackButton; 49 RollbackManager mRollbackManager; 50 static final String ROLLBACK_ID_EXTRA = "rollbackId"; 51 static final String ACTION_NAME = MainActivity.class.getName(); 52 53 @Override onCreate(Bundle savedInstanceState)54 public void onCreate(Bundle savedInstanceState) { 55 super.onCreate(savedInstanceState); 56 setContentView(R.layout.activity_main); 57 ListView rollbackListView = findViewById(R.id.listView); 58 mRollbackManager = getApplicationContext().getSystemService(RollbackManager.class); 59 initTriggerRollbackButton(); 60 61 // Populate list of available rollbacks. 62 List<RollbackInfo> availableRollbacks = mRollbackManager.getAvailableRollbacks(); 63 CustomAdapter adapter = new CustomAdapter(availableRollbacks); 64 rollbackListView.setAdapter(adapter); 65 66 // Register receiver for rollback status events. 67 getApplicationContext().registerReceiver( 68 new BroadcastReceiver() { 69 @Override 70 public void onReceive(Context context, 71 Intent intent) { 72 int rollbackId = intent.getIntExtra(ROLLBACK_ID_EXTRA, -1); 73 int rollbackStatusCode = intent.getIntExtra(RollbackManager.EXTRA_STATUS, 74 RollbackManager.STATUS_FAILURE); 75 String rollbackStatus = "FAILED"; 76 if (rollbackStatusCode == RollbackManager.STATUS_SUCCESS) { 77 rollbackStatus = "SUCCESS"; 78 mTriggerRollbackButton.setClickable(false); 79 } 80 makeToast("Status for rollback ID " + rollbackId + " is " + rollbackStatus); 81 }}, new IntentFilter(ACTION_NAME), Context.RECEIVER_NOT_EXPORTED); 82 } 83 initTriggerRollbackButton()84 private void initTriggerRollbackButton() { 85 mTriggerRollbackButton = findViewById(R.id.trigger_rollback_button); 86 mTriggerRollbackButton.setClickable(false); 87 mTriggerRollbackButton.setOnClickListener(v -> { 88 // Commits all selected rollbacks. Rollback status events will be sent to our receiver. 89 for (int i = 0; i < mIdsToRollback.size(); i++) { 90 Intent intent = new Intent(ACTION_NAME); 91 intent.putExtra(ROLLBACK_ID_EXTRA, mIdsToRollback.get(i)); 92 intent.setPackage(getApplicationContext().getPackageName()); 93 PendingIntent pendingIntent = PendingIntent.getBroadcast( 94 getApplicationContext(), 0, intent, FLAG_MUTABLE); 95 mRollbackManager.commitRollback(mIdsToRollback.get(i), 96 Collections.emptyList(), 97 pendingIntent.getIntentSender()); 98 } 99 }); 100 } 101 102 103 makeToast(String message)104 private void makeToast(String message) { 105 runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show()); 106 } 107 108 public class CustomAdapter extends BaseAdapter { 109 List<RollbackInfo> mRollbackInfos; 110 LayoutInflater mInflater = LayoutInflater.from(getApplicationContext()); 111 CustomAdapter(List<RollbackInfo> rollbackInfos)112 CustomAdapter(List<RollbackInfo> rollbackInfos) { 113 mRollbackInfos = rollbackInfos; 114 } 115 116 @Override getCount()117 public int getCount() { 118 return mRollbackInfos.size(); 119 } 120 121 @Override getItem(int position)122 public Object getItem(int position) { 123 return mRollbackInfos.get(position); 124 } 125 126 @Override getItemId(int position)127 public long getItemId(int position) { 128 return mRollbackInfos.get(position).getRollbackId(); 129 } 130 131 @Override getView(int position, View view, ViewGroup parent)132 public View getView(int position, View view, ViewGroup parent) { 133 if (view == null) { 134 view = mInflater.inflate(R.layout.listitem_rollbackinfo, null); 135 } 136 RollbackInfo rollbackInfo = mRollbackInfos.get(position); 137 TextView rollbackIdView = view.findViewById(R.id.rollback_id); 138 rollbackIdView.setText("Rollback ID " + rollbackInfo.getRollbackId()); 139 TextView rollbackPackagesTextView = view.findViewById(R.id.rollback_packages); 140 StringBuilder sb = new StringBuilder(); 141 for (int i = 0; i < rollbackInfo.getPackages().size(); i++) { 142 PackageRollbackInfo pkgInfo = rollbackInfo.getPackages().get(i); 143 sb.append(pkgInfo.getPackageName() + ": " 144 + pkgInfo.getVersionRolledBackFrom().getLongVersionCode() + " -> " 145 + pkgInfo.getVersionRolledBackTo().getLongVersionCode() + ","); 146 } 147 sb.deleteCharAt(sb.length() - 1); 148 rollbackPackagesTextView.setText(sb.toString()); 149 CheckBox checkbox = view.findViewById(R.id.checkbox); 150 checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> { 151 if (isChecked) { 152 mIdsToRollback.add(rollbackInfo.getRollbackId()); 153 } else { 154 mIdsToRollback.remove(Integer.valueOf(rollbackInfo.getRollbackId())); 155 } 156 mTriggerRollbackButton.setClickable(mIdsToRollback.size() > 0); 157 }); 158 return view; 159 } 160 } 161 } 162