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.streamitems;
18
19import android.app.Activity;
20import android.content.ContentProviderOperation;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Intent;
24import android.database.Cursor;
25import android.net.Uri;
26import android.os.Bundle;
27import android.provider.ContactsContract;
28import android.provider.ContactsContract.RawContacts;
29import android.provider.ContactsContract.StreamItemPhotos;
30import android.provider.ContactsContract.StreamItems;
31import android.view.View;
32import android.widget.Button;
33import android.widget.Toast;
34
35import com.android.contacts.model.account.GoogleAccountType;
36import com.android.contacts.tests.R;
37import com.google.common.collect.Lists;
38
39import java.io.IOException;
40import java.io.InputStream;
41import java.util.ArrayList;
42import java.util.Random;
43
44/**
45 * Testing activity that will populate stream items and stream item photos to selected
46 * entries in the user's contacts list.
47 *
48 * The contact selected must have at least one raw contact that was provided by Google.
49 */
50public class StreamItemPopulatorActivity extends Activity {
51
52    // Test data to randomly select from.
53    private String[] snippetStrings = new String[]{
54            "Just got back from a vacation in %1$s - what a great place!  Can't wait to go back.",
55            "If I never see %1$s again it will be too soon.",
56            "This is a public service announcement.  If you were even close to considering visiting"
57            + " %1$s, I strongly advise you to reconsider.  The food was terrible, the people were "
58            + "rude, the hygiene of the bus and taxi drivers was positively <i>barbaric</i>.  I "
59            + "feared for my life almost the entire time I was there, and feel lucky to be back "
60            + "<b>home</b>.",
61            "Check out these pictures!  I took them in %1$s"
62    };
63
64    private String[] placeNames = new String[]{
65            "the Google campus in Mountain View",
66            "the deserts on Arrakis",
67            "Iceland",
68            "Japan",
69            "Sydney",
70            "San Francisco",
71            "Munich",
72            "Istanbul",
73            "Tanagra",
74            "the restricted section of Area 51",
75            "the middle of nowhere"
76    };
77
78    private String[] commentStrings = new String[]{
79            "3 retweets",
80            "5 shares",
81            "4 likes",
82            "4 +1s",
83            "<i>24567</i> <font color='blue' size='+1'><b>likes</b></font>"
84    };
85
86    private String[] labelResources = new String[] {
87            "attribution_google_plus",
88            "attribution_google_talk",
89            "attribution_flicker",
90            "attribution_twitter"
91    };
92
93    public String[] iconResources = new String[] {
94            "default_icon"
95    };
96
97    // Photos to randomly select from.
98    private Integer[] imageIds = new Integer[]{
99            R.drawable.android,
100            R.drawable.goldengate,
101            R.drawable.iceland,
102            R.drawable.japan,
103            R.drawable.sydney,
104            R.drawable.wharf,
105            R.drawable.whiskey
106    };
107
108    // Only some photos have actions.
109    private String[] imageStrings = new String[]{
110            "android",
111            "goldengate",
112            "iceland",
113            "japan",
114    };
115
116    // The contact ID that was picked.
117    private long mContactId = -1;
118
119    private Random mRandom;
120
121    @Override
122    protected void onCreate(Bundle savedInstanceState) {
123        super.onCreate(savedInstanceState);
124
125        mRandom = new Random(System.currentTimeMillis());
126
127        setContentView(R.layout.stream_item_populator);
128        Button pickButton = (Button) findViewById(R.id.add);
129        pickButton.setOnClickListener(new View.OnClickListener(){
130            @Override
131            public void onClick(View v) {
132                // Reset the contact ID.
133                mContactId = -1;
134
135                // Forward the Intent to the picker
136                final Intent pickerIntent =
137                        new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
138                pickerIntent.setFlags(
139                        Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
140                startActivityForResult(pickerIntent, 0);
141            }
142        });
143
144        Button exitButton = (Button) findViewById(R.id.exit);
145        exitButton.setOnClickListener(new View.OnClickListener() {
146            @Override
147            public void onClick(View v) {
148                finish();
149            }
150        });
151    }
152
153    @Override
154    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
155        if (resultCode == Activity.RESULT_OK) {
156            Uri contactUri = data.getData();
157            mContactId = ContentUris.parseId(contactUri);
158        }
159    }
160
161    @Override
162    protected void onResume() {
163        super.onResume();
164        if (mContactId != -1) {
165            long rawContactId = -1;
166            String accountType = null;
167            String accountName = null;
168            String dataSet = null;
169
170            // Lookup the com.google raw contact for the contact.
171            Cursor c = getContentResolver().query(RawContacts.CONTENT_URI,
172                    new String[]{
173                            RawContacts._ID,
174                            RawContacts.ACCOUNT_TYPE,
175                            RawContacts.ACCOUNT_NAME
176                    },
177                    RawContacts.CONTACT_ID + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?",
178                    new String[]{String.valueOf(mContactId), GoogleAccountType.ACCOUNT_TYPE}, null);
179            try {
180                c.moveToFirst();
181                rawContactId = c.getLong(0);
182                accountType = c.getString(1);
183                accountName = c.getString(2);
184            } finally {
185                c.close();
186            }
187            if (rawContactId != -1) {
188                addStreamItemsToRawContact(rawContactId, accountType, accountName);
189            } else {
190                Toast.makeText(this,
191                        "Failed to find raw contact ID for contact ID " + mContactId, 5).show();
192            }
193        }
194    }
195
196    protected byte[] loadPhotoFromResource(int resourceId) {
197        InputStream is = getResources().openRawResource(resourceId);
198        return readInputStreamFully(is);
199    }
200
201    protected byte[] readInputStreamFully(InputStream is) {
202        try {
203            byte[] buffer = new byte[is.available()];
204            is.read(buffer);
205            is.close();
206            return buffer;
207        } catch (IOException e) {
208            throw new RuntimeException(e);
209        }
210    }
211
212    private void addStreamItemsToRawContact(long rawContactId, String accountType,
213            String accountName) {
214        ArrayList<ContentProviderOperation> ops = Lists.newArrayList();
215
216        // Add from 1-5 stream items.
217        int itemsToAdd = randInt(5) + 1;
218        int opCount = 0;
219        for (int i = 0; i < itemsToAdd; i++) {
220            ContentValues streamItemValues = buildStreamItemValues(accountType, accountName);
221            ops.add(ContentProviderOperation.newInsert(
222                    Uri.withAppendedPath(
223                            ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI,
224                                    rawContactId),
225                            ContactsContract.RawContacts.StreamItems.CONTENT_DIRECTORY))
226                    .withValues(streamItemValues).build());
227
228            // Maybe add photos - 30% chance per stream item.
229            boolean includePhotos = randInt(100) < 30;
230            if (includePhotos) {
231                // Add 1-5 photos if we're including any.
232                int numPhotos = randInt(5) + 1;
233                for (int j = 0; j < numPhotos; j++) {
234                    ContentValues streamItemPhotoValues =
235                            buildStreamItemPhotoValues(j, accountType, accountName);
236                    ops.add(ContentProviderOperation.newInsert(StreamItems.CONTENT_PHOTO_URI)
237                            .withValues(streamItemPhotoValues)
238                            .withValueBackReference(StreamItemPhotos.STREAM_ITEM_ID, opCount)
239                            .build());
240                }
241                opCount += numPhotos;
242            }
243            opCount++;
244        }
245        try {
246            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
247        } catch (Exception e) {
248            // We don't care.  This is just for test purposes.
249            throw new RuntimeException(e);
250        }
251        Toast.makeText(this, "Added " + itemsToAdd + " stream item(s) and "
252                + (opCount - itemsToAdd) + " photos", 5).show();
253    }
254
255    private ContentValues buildStreamItemValues(String accountType, String accountName) {
256        boolean includeAttribution = randInt(100) < 70;
257        boolean includeComments = randInt(100) < 30;
258        boolean includeAction = randInt(100) < 30;
259        ContentValues values = new ContentValues();
260        String place = pickRandom(placeNames);
261        values.put(StreamItems.TEXT,
262                String.format(pickRandom(snippetStrings) , place)
263                + (includeComments ? " [c]" : "")
264                + (includeAction ? " [a]" : ""));
265        if (includeAttribution) {
266            values.put(StreamItems.RES_PACKAGE, "com.android.contacts.tests");
267            int sourceIndex = randInt(labelResources.length);
268            values.put(StreamItems.RES_LABEL, labelResources[sourceIndex]);
269            if (sourceIndex < iconResources.length) {
270                values.put(StreamItems.RES_ICON, iconResources[sourceIndex]);
271            }
272        }
273        if (includeComments) {
274            values.put(StreamItems.COMMENTS, pickRandom(commentStrings));
275        } else {
276            values.put(StreamItems.COMMENTS, "");
277        }
278        // Set the timestamp to some point in the past.
279        values.put(StreamItems.TIMESTAMP,
280                System.currentTimeMillis() - randInt(360000000));
281        values.put(RawContacts.ACCOUNT_TYPE, accountType);
282        values.put(RawContacts.ACCOUNT_NAME, accountName);
283        return values;
284    }
285
286    private ContentValues buildStreamItemPhotoValues(int index, String accountType,
287            String accountName) {
288        Integer imageIndex = pickRandom(imageIds);
289        ContentValues values = new ContentValues();
290        values.put(StreamItemPhotos.SORT_INDEX, index);
291        values.put(StreamItemPhotos.PHOTO, loadPhotoFromResource(imageIndex));
292        values.put(RawContacts.ACCOUNT_TYPE, accountType);
293        values.put(RawContacts.ACCOUNT_NAME, accountName);
294        return values;
295    }
296
297    private <T> T pickRandom(T[] from) {
298        return from[randInt(from.length)];
299    }
300
301    private int randInt(int max) {
302        return Math.abs(mRandom.nextInt()) % max;
303    }
304}
305