FillCallLogTestActivity.java revision e0b2f1e2d01d1ac52ba207dc7ce76971d853298e
1/*
2 * Copyright (C) 2011 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
17package com.android.contacts.tests.calllog;
18
19import android.app.Activity;
20import android.app.LoaderManager;
21import android.content.ContentProviderClient;
22import android.content.ContentValues;
23import android.content.CursorLoader;
24import android.content.Loader;
25import android.database.Cursor;
26import android.os.AsyncTask;
27import android.os.Bundle;
28import android.os.RemoteException;
29import android.provider.CallLog.Calls;
30import android.util.Log;
31import android.view.View;
32import android.widget.Button;
33import android.widget.CheckBox;
34import android.widget.ProgressBar;
35import android.widget.TextView;
36import android.widget.Toast;
37
38import com.android.contacts.tests.R;
39
40import java.util.Random;
41
42/**
43 * Activity to add entries to the call log for testing.
44 */
45public class FillCallLogTestActivity extends Activity {
46    private static final String TAG = "FillCallLogTestActivity";
47    /** Identifier of the loader for querying the call log. */
48    private static final int CALLLOG_LOADER_ID = 1;
49
50    private static final Random RNG = new Random();
51    private static final int[] CALL_TYPES = new int[] {
52        Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE, Calls.MISSED_TYPE,
53    };
54
55    private TextView mNumberTextView;
56    private Button mAddButton;
57    private ProgressBar mProgressBar;
58    private CheckBox mUseRandomNumbers;
59
60    @Override
61    protected void onCreate(Bundle savedInstanceState) {
62        super.onCreate(savedInstanceState);
63        setContentView(R.layout.fill_call_log_test);
64        mNumberTextView = (TextView) findViewById(R.id.number);
65        mAddButton = (Button) findViewById(R.id.add);
66        mProgressBar = (ProgressBar) findViewById(R.id.progress);
67        mUseRandomNumbers = (CheckBox) findViewById(R.id.use_random_numbers);
68
69        mAddButton.setOnClickListener(new View.OnClickListener(){
70            @Override
71            public void onClick(View v) {
72                int count;
73                try {
74                    count = Integer.parseInt(mNumberTextView.getText().toString());
75                    if (count > 100) {
76                        throw new RuntimeException("Number too large.  Max=100");
77                    }
78                } catch (RuntimeException e) {
79                    Toast.makeText(FillCallLogTestActivity.this, e.toString(), Toast.LENGTH_LONG)
80                            .show();
81                    return;
82                }
83                addEntriesToCallLog(count, mUseRandomNumbers.isChecked());
84                mNumberTextView.setEnabled(false);
85                mAddButton.setEnabled(false);
86                mProgressBar.setProgress(0);
87                mProgressBar.setMax(count);
88                mProgressBar.setVisibility(View.VISIBLE);
89            }
90        });
91    }
92
93    /**
94     * Adds a number of entries to the call log. The content of the entries is based on existing
95     * entries.
96     *
97     * @param count the number of entries to add
98     */
99    private void addEntriesToCallLog(final int count, boolean useRandomNumbers) {
100        if (useRandomNumbers) {
101            addRandomNumbers(count);
102        } else {
103            getLoaderManager().initLoader(CALLLOG_LOADER_ID, null,
104                    new CallLogLoaderListener(count));
105        }
106    }
107
108    /**
109     * Calls when the insertion has completed.
110     *
111     * @param message the message to show in a toast to the user
112     */
113    private void insertCompleted(String message) {
114        // Hide the progress bar.
115        mProgressBar.setVisibility(View.GONE);
116        // Re-enable the add button.
117        mNumberTextView.setEnabled(true);
118        mAddButton.setEnabled(true);
119        mNumberTextView.setText("");
120        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
121    }
122
123
124    /**
125     * Creates a {@link ContentValues} object containing values corresponding to the given cursor.
126     *
127     * @param cursor the cursor from which to get the values
128     * @return a newly created content values object
129     */
130    private ContentValues createContentValuesFromCursor(Cursor cursor) {
131        ContentValues values = new ContentValues();
132        for (int column = 0; column < cursor.getColumnCount();
133                ++column) {
134            String name = cursor.getColumnName(column);
135            switch (cursor.getType(column)) {
136                case Cursor.FIELD_TYPE_STRING:
137                    values.put(name, cursor.getString(column));
138                    break;
139                case Cursor.FIELD_TYPE_INTEGER:
140                    values.put(name, cursor.getLong(column));
141                    break;
142                case Cursor.FIELD_TYPE_FLOAT:
143                    values.put(name, cursor.getDouble(column));
144                    break;
145                case Cursor.FIELD_TYPE_BLOB:
146                    values.put(name, cursor.getBlob(column));
147                    break;
148                case Cursor.FIELD_TYPE_NULL:
149                    values.putNull(name);
150                    break;
151                default:
152                    Log.d(TAG, "Invalid value in cursor: " + cursor.getType(column));
153                    break;
154            }
155        }
156        return values;
157    }
158
159    private void addRandomNumbers(int count) {
160        ContentValues[] values = new ContentValues[count];
161        for (int i = 0; i < count; i++) {
162            values[i] = new ContentValues();
163            values[i].put(Calls.NUMBER, generateRandomNumber());
164            values[i].put(Calls.DATE, System.currentTimeMillis()); // Will be randomized later
165            values[i].put(Calls.DURATION, 1); // Will be overwritten later
166        }
167        new AsyncCallLogInserter(values).execute(new Void[0]);
168    }
169
170    private static String generateRandomNumber() {
171        return String.format("5%09d", RNG.nextInt(1000000000));
172    }
173
174    /** Invokes {@link AsyncCallLogInserter} when the call log has loaded. */
175    private final class CallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
176        /** The number of items to insert when done. */
177        private final int mCount;
178
179        private CallLogLoaderListener(int count) {
180            mCount = count;
181        }
182
183        @Override
184        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
185            Log.d(TAG, "onCreateLoader");
186            return new CursorLoader(FillCallLogTestActivity.this, Calls.CONTENT_URI,
187                    null, null, null, null);
188        }
189
190        @Override
191        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
192            try {
193                Log.d(TAG, "onLoadFinished");
194
195                if (data.getCount() == 0) {
196                    // If there are no entries in the call log, we cannot generate new ones.
197                    insertCompleted(getString(R.string.noLogEntriesToast));
198                    return;
199                }
200
201                data.moveToPosition(-1);
202
203                ContentValues[] values = new ContentValues[mCount];
204                for (int index = 0; index < mCount; ++index) {
205                    if (!data.moveToNext()) {
206                        data.moveToFirst();
207                    }
208                    values[index] = createContentValuesFromCursor(data);
209                }
210                new AsyncCallLogInserter(values).execute(new Void[0]);
211            } finally {
212                // This is a one shot loader.
213                getLoaderManager().destroyLoader(CALLLOG_LOADER_ID);
214            }
215        }
216
217        @Override
218        public void onLoaderReset(Loader<Cursor> loader) {}
219    }
220
221    /** Inserts a given number of entries in the call log based on the values given. */
222    private final class AsyncCallLogInserter extends AsyncTask<Void, Integer, Integer> {
223        /** The number of items to insert. */
224        private final ContentValues[] mValues;
225
226        public AsyncCallLogInserter(ContentValues[] values) {
227            mValues = values;
228        }
229
230        @Override
231        protected Integer doInBackground(Void... params) {
232            Log.d(TAG, "doInBackground");
233            return insertIntoCallLog();
234        }
235
236        @Override
237        protected void onProgressUpdate(Integer... values) {
238            Log.d(TAG, "onProgressUpdate");
239            updateCount(values[0]);
240        }
241
242        @Override
243        protected void onPostExecute(Integer count) {
244            Log.d(TAG, "onPostExecute");
245            insertCompleted(getString(R.string.addedLogEntriesToast, count));
246        }
247
248        /**
249         * Inserts a number of entries in the call log based on the given templates.
250         *
251         * @return the number of inserted entries
252         */
253        private Integer insertIntoCallLog() {
254            int inserted = 0;
255
256            for (int index = 0; index < mValues.length; ++index) {
257                ContentValues values = mValues[index];
258                // These should not be set.
259                values.putNull(Calls._ID);
260                // Add some randomness to the date. For each new entry being added, add an extra
261                // day to the maximum possible offset from the original.
262                values.put(Calls.DATE,
263                        values.getAsLong(Calls.DATE)
264                        - RNG.nextInt(24 * 60 * 60 * (index + 1)) * 1000L);
265                // Add some randomness to the duration.
266                if (values.getAsLong(Calls.DURATION) > 0) {
267                    values.put(Calls.DURATION, RNG.nextInt(30 * 60 * 60 * 1000));
268                }
269
270                // Overwrite type.
271                values.put(Calls.TYPE, CALL_TYPES[RNG.nextInt(CALL_TYPES.length)]);
272
273                // Clear cached columns.
274                values.putNull(Calls.CACHED_FORMATTED_NUMBER);
275                values.putNull(Calls.CACHED_LOOKUP_URI);
276                values.putNull(Calls.CACHED_MATCHED_NUMBER);
277                values.putNull(Calls.CACHED_NAME);
278                values.putNull(Calls.CACHED_NORMALIZED_NUMBER);
279                values.putNull(Calls.CACHED_NUMBER_LABEL);
280                values.putNull(Calls.CACHED_NUMBER_TYPE);
281                values.putNull(Calls.CACHED_PHOTO_ID);
282
283                // Insert into the call log the newly generated entry.
284                ContentProviderClient contentProvider =
285                        getContentResolver().acquireContentProviderClient(
286                                Calls.CONTENT_URI);
287                try {
288                    Log.d(TAG, "adding entry to call log");
289                    contentProvider.insert(Calls.CONTENT_URI, values);
290                    ++inserted;
291                    this.publishProgress(inserted);
292                } catch (RemoteException e) {
293                    Log.d(TAG, "insert failed", e);
294                }
295            }
296            return inserted;
297        }
298    }
299
300    /**
301     * Updates the count shown to the user corresponding to the number of entries added.
302     *
303     * @param count the number of entries inserted so far
304     */
305    public void updateCount(Integer count) {
306        mProgressBar.setProgress(count);
307    }
308}
309