1 /* 2 * Copyright (C) 2018 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.tests.usagereporter; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.ListActivity; 22 import android.app.usage.UsageStatsManager; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.os.Bundle; 26 import android.text.InputType; 27 import android.text.TextUtils; 28 import android.util.ArraySet; 29 import android.view.LayoutInflater; 30 import android.view.Menu; 31 import android.view.MenuInflater; 32 import android.view.MenuItem; 33 import android.view.View; 34 import android.view.View.OnClickListener; 35 import android.view.ViewGroup; 36 import android.widget.BaseAdapter; 37 import android.widget.Button; 38 import android.widget.EditText; 39 import android.widget.TextView; 40 import android.widget.Toast; 41 42 import java.util.ArrayList; 43 44 public class UsageReporterActivity extends ListActivity { 45 46 private Activity mActivity; 47 private final ArrayList<String> mTokens = new ArrayList(); 48 private final ArraySet<String> mActives = new ArraySet(); 49 private UsageStatsManager mUsageStatsManager; 50 private Adapter mAdapter; 51 private boolean mRestoreOnStart = false; 52 private static Context sContext; 53 54 /** Called with the activity is first created. */ 55 @Override onCreate(Bundle savedInstanceState)56 public void onCreate(Bundle savedInstanceState) { 57 super.onCreate(savedInstanceState); 58 59 sContext = getApplicationContext(); 60 61 mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); 62 63 mAdapter = new Adapter(); 64 setListAdapter(mAdapter); 65 } 66 67 @Override onStart()68 protected void onStart() { 69 super.onStart(); 70 mActivity = this; 71 72 73 if (mRestoreOnStart) { 74 ArrayList<String> removed = null; 75 for (String token : mActives) { 76 try { 77 mUsageStatsManager.reportUsageStart(mActivity, token); 78 } catch (Exception e) { 79 // Somthing went wrong, recover and move on 80 if (removed == null) { 81 removed = new ArrayList(); 82 } 83 removed.add(token); 84 } 85 } 86 if (removed != null) { 87 for (String token : removed) { 88 mActives.remove(token); 89 } 90 } 91 } else { 92 mActives.clear(); 93 } 94 } 95 96 /** 97 * Called when the activity is about to start interacting with the user. 98 */ 99 @Override onResume()100 protected void onResume() { 101 super.onResume(); 102 mAdapter.notifyDataSetChanged(); 103 } 104 105 106 /** 107 * Called when your activity's options menu needs to be created. 108 */ 109 @Override onCreateOptionsMenu(Menu menu)110 public boolean onCreateOptionsMenu(Menu menu) { 111 MenuInflater inflater = getMenuInflater(); 112 inflater.inflate(R.menu.main, menu); 113 return super.onCreateOptionsMenu(menu); 114 } 115 116 117 /** 118 * Called when a menu item is selected. 119 */ 120 @Override onOptionsItemSelected(MenuItem item)121 public boolean onOptionsItemSelected(MenuItem item) { 122 switch (item.getItemId()) { 123 case R.id.add_token: 124 callAddToken(); 125 return true; 126 case R.id.add_many_tokens: 127 callAddManyTokens(); 128 return true; 129 case R.id.stop_all: 130 callStopAll(); 131 return true; 132 case R.id.restore_on_start: 133 mRestoreOnStart = !mRestoreOnStart; 134 item.setChecked(mRestoreOnStart); 135 return true; 136 } 137 138 return super.onOptionsItemSelected(item); 139 } 140 callAddToken()141 private void callAddToken() { 142 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 143 builder.setTitle(getString(R.string.token_query)); 144 final EditText input = new EditText(this); 145 input.setInputType(InputType.TYPE_CLASS_TEXT); 146 input.setHint(getString(R.string.default_token)); 147 builder.setView(input); 148 149 builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { 150 @Override 151 public void onClick(DialogInterface dialog, int which) { 152 String tokenNames = input.getText().toString().trim(); 153 if (TextUtils.isEmpty(tokenNames)) { 154 tokenNames = getString(R.string.default_token); 155 } 156 String[] tokens = tokenNames.split(","); 157 for (String token : tokens) { 158 if (mTokens.contains(token)) continue; 159 mTokens.add(token); 160 } 161 mAdapter.notifyDataSetChanged(); 162 163 } 164 }); 165 builder.setNegativeButton(getString(R.string.cancel), 166 new DialogInterface.OnClickListener() { 167 @Override 168 public void onClick(DialogInterface dialog, int which) { 169 dialog.cancel(); 170 } 171 }); 172 173 builder.show(); 174 } 175 callAddManyTokens()176 private void callAddManyTokens() { 177 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 178 builder.setTitle(getString(R.string.many_tokens_query)); 179 final EditText input = new EditText(this); 180 input.setInputType(InputType.TYPE_CLASS_NUMBER); 181 builder.setView(input); 182 183 builder.setPositiveButton(getString(R.string.ok), 184 new DialogInterface.OnClickListener() { 185 @Override 186 public void onClick(DialogInterface dialog, int which) { 187 String val = input.getText().toString().trim(); 188 if (TextUtils.isEmpty(val)) return; 189 int n = Integer.parseInt(val); 190 for (int i = 0; i < n; i++) { 191 final String token = getString(R.string.default_token) + i; 192 if (mTokens.contains(token)) continue; 193 mTokens.add(token); 194 } 195 mAdapter.notifyDataSetChanged(); 196 197 } 198 }); 199 builder.setNegativeButton(getString(R.string.cancel), 200 new DialogInterface.OnClickListener() { 201 @Override 202 public void onClick(DialogInterface dialog, int which) { 203 dialog.cancel(); 204 } 205 }); 206 207 builder.show(); 208 } 209 callStopAll()210 private void callStopAll() { 211 final AlertDialog.Builder builder = new AlertDialog.Builder(this); 212 builder.setTitle(getString(R.string.stop_all_tokens_query)); 213 214 builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { 215 @Override 216 public void onClick(DialogInterface dialog, int which) { 217 for (String token : mActives) { 218 mUsageStatsManager.reportUsageStop(mActivity, token); 219 } 220 mActives.clear(); 221 mAdapter.notifyDataSetChanged(); 222 } 223 }); 224 builder.setNegativeButton(getString(R.string.cancel), 225 new DialogInterface.OnClickListener() { 226 @Override 227 public void onClick(DialogInterface dialog, int which) { 228 dialog.cancel(); 229 } 230 }); 231 builder.show(); 232 } 233 234 /** 235 * A call-back for when the user presses the back button. 236 */ 237 OnClickListener mStartListener = new OnClickListener() { 238 public void onClick(View v) { 239 final View parent = (View) v.getParent(); 240 final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString(); 241 try { 242 mUsageStatsManager.reportUsageStart(mActivity, token); 243 } catch (Exception e) { 244 Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show(); 245 } 246 parent.setBackgroundColor(getColor(R.color.active_color)); 247 mActives.add(token); 248 } 249 }; 250 251 /** 252 * A call-back for when the user presses the clear button. 253 */ 254 OnClickListener mStopListener = new OnClickListener() { 255 public void onClick(View v) { 256 final View parent = (View) v.getParent(); 257 258 final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString(); 259 try { 260 mUsageStatsManager.reportUsageStop(mActivity, token); 261 } catch (Exception e) { 262 Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show(); 263 } 264 parent.setBackgroundColor(getColor(R.color.inactive_color)); 265 mActives.remove(token); 266 } 267 }; 268 269 270 private class Adapter extends BaseAdapter { 271 @Override getCount()272 public int getCount() { 273 return mTokens.size(); 274 } 275 276 @Override getItem(int position)277 public Object getItem(int position) { 278 return mTokens.get(position); 279 } 280 281 @Override getItemId(int position)282 public long getItemId(int position) { 283 return position; 284 } 285 286 @Override getView(int position, View convertView, ViewGroup parent)287 public View getView(int position, View convertView, ViewGroup parent) { 288 final ViewHolder holder; 289 if (convertView == null) { 290 convertView = LayoutInflater.from(UsageReporterActivity.this) 291 .inflate(R.layout.row_item, parent, false); 292 holder = new ViewHolder(); 293 holder.tokenName = (TextView) convertView.findViewById(R.id.token); 294 295 holder.startButton = ((Button) convertView.findViewById(R.id.start)); 296 holder.startButton.setOnClickListener(mStartListener); 297 holder.stopButton = ((Button) convertView.findViewById(R.id.stop)); 298 holder.stopButton.setOnClickListener(mStopListener); 299 convertView.setTag(holder); 300 } else { 301 holder = (ViewHolder) convertView.getTag(); 302 } 303 304 final String token = mTokens.get(position); 305 holder.tokenName.setText(mTokens.get(position)); 306 if (mActives.contains(token)) { 307 convertView.setBackgroundColor(getColor(R.color.active_color)); 308 } else { 309 convertView.setBackgroundColor(getColor(R.color.inactive_color)); 310 } 311 return convertView; 312 } 313 } 314 315 private static class ViewHolder { 316 public TextView tokenName; 317 public Button startButton; 318 public Button stopButton; 319 } 320 } 321