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