diff options
author | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-24 22:53:16 +0100 |
---|---|---|
committer | Ricardo Cerqueira <cyanogenmod@cerqueira.org> | 2013-07-24 22:53:16 +0100 |
commit | 5ae14b02bacf85e848eb6aa507a38c8f18c2f595 (patch) | |
tree | 0087a8f58db5256b85716cb434b465c1f2da40d8 | |
parent | aff1242e5451db7edf4fc165895b656dea722593 (diff) | |
parent | f881a0565252fe947775d499768ef6a46490348a (diff) | |
download | android_frameworks_ex-5ae14b02bacf85e848eb6aa507a38c8f18c2f595.tar.gz android_frameworks_ex-5ae14b02bacf85e848eb6aa507a38c8f18c2f595.tar.bz2 android_frameworks_ex-5ae14b02bacf85e848eb6aa507a38c8f18c2f595.zip |
Merge tag 'android-4.3_r2.1' into cm-10.2
Android 4.3 release 2.1
169 files changed, 1411 insertions, 6484 deletions
diff --git a/carousel/test/res/values-in/strings.xml b/carousel/test/res/values-in/strings.xml index 40000e8..06c8812 100644 --- a/carousel/test/res/values-in/strings.xml +++ b/carousel/test/res/values-in/strings.xml @@ -21,7 +21,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="music_demo_activity_label" msgid="4382090808250495841">"KaruselMusik"</string> <string name="carousel_test_activity_label" msgid="6014624482213318747">"UjiKarusel"</string> - <string name="carousel_test_activity_description" msgid="1632693812604375483">"Aplikasi untuk menampilkan penggunaan Karusel"</string> + <string name="carousel_test_activity_description" msgid="1632693812604375483">"Aplikasi untuk menampilkan penggunaan Korsel"</string> <string name="task_switcher_activity_label" msgid="714620143340933546">"PengubahTugas"</string> <string name="recent_tasks_title" msgid="1030287226205477117">"Aplikasi Terbaru"</string> <string name="no_recent_tasks" msgid="6884096266670555780">"Tidak ada tugas terbaru"</string> diff --git a/chips/Android.mk b/chips/Android.mk index 4a7977a..6aa8a01 100644 --- a/chips/Android.mk +++ b/chips/Android.mk @@ -21,4 +21,9 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ $(call all-logtags-files-under, src) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file +include $(BUILD_STATIC_JAVA_LIBRARY) + +################################################## +# Build all sub-directories + +include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file diff --git a/chips/res/drawable-hdpi/ic_contact_picture.png b/chips/res/drawable-hdpi/ic_contact_picture.png Binary files differindex 2eef7b5..4c0e35e 100644 --- a/chips/res/drawable-hdpi/ic_contact_picture.png +++ b/chips/res/drawable-hdpi/ic_contact_picture.png diff --git a/chips/res/drawable-mdpi/ic_contact_picture.png b/chips/res/drawable-mdpi/ic_contact_picture.png Binary files differindex 6c7cb61..ead9718 100644 --- a/chips/res/drawable-mdpi/ic_contact_picture.png +++ b/chips/res/drawable-mdpi/ic_contact_picture.png diff --git a/chips/res/drawable-xhdpi/ic_contact_picture.png b/chips/res/drawable-xhdpi/ic_contact_picture.png Binary files differindex 1a2bfde..05a65f6 100644 --- a/chips/res/drawable-xhdpi/ic_contact_picture.png +++ b/chips/res/drawable-xhdpi/ic_contact_picture.png diff --git a/chips/res/values-ko/strings.xml b/chips/res/values-ko/strings.xml index 7423ce5..f7884bd 100644 --- a/chips/res/values-ko/strings.xml +++ b/chips/res/values-ko/strings.xml @@ -19,5 +19,5 @@ <string name="more_string" msgid="8495478259330621990">"<xliff:g id="COUNT">%1$s</xliff:g>명 이상"</string> <string name="copy_email" msgid="7869435992461603532">"이메일 주소 복사"</string> <string name="copy_number" msgid="530057841276106843">"전화번호 복사"</string> - <string name="done" msgid="2356320650733788862">"Enter 키"</string> + <string name="done" msgid="2356320650733788862">"입력"</string> </resources> diff --git a/chips/res/values-pt-rPT/strings.xml b/chips/res/values-pt-rPT/strings.xml index bfbe1ca..fc991b1 100644 --- a/chips/res/values-pt-rPT/strings.xml +++ b/chips/res/values-pt-rPT/strings.xml @@ -19,5 +19,5 @@ <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string> <string name="copy_email" msgid="7869435992461603532">"Copiar endereço de email"</string> <string name="copy_number" msgid="530057841276106843">"Copiar número de telefone"</string> - <string name="done" msgid="2356320650733788862">"Regressar"</string> + <string name="done" msgid="2356320650733788862">"Voltar"</string> </resources> diff --git a/chips/res/values/dimen.xml b/chips/res/values/dimen.xml index b93555f..98354d2 100644 --- a/chips/res/values/dimen.xml +++ b/chips/res/values/dimen.xml @@ -19,4 +19,5 @@ <dimen name="chip_height">32dip</dimen> <dimen name="chip_text_size">14sp</dimen> <dimen name="line_spacing_extra">4dip</dimen> + <integer name="chips_max_lines">-1</integer> </resources>
\ No newline at end of file diff --git a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java index c981728..71b610e 100644 --- a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java +++ b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java @@ -73,12 +73,12 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter * The number of extra entries requested to allow for duplicates. Duplicates * are removed from the overall result. */ - private static final int ALLOWANCE_FOR_DUPLICATES = 5; + static final int ALLOWANCE_FOR_DUPLICATES = 5; // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden - private static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account"; + static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account"; // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden - private static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account"; + static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account"; /** The number of photos cached in this Adapter. */ private static final int PHOTO_CACHE_SIZE = 20; @@ -118,7 +118,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter public static final int PHOTO = 0; } - private static class DirectoryListQuery { + protected static class DirectoryListQuery { public static final Uri URI = Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories"); @@ -234,8 +234,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter } // We'll copy this result to mEntry in publicResults() (run in the UX thread). - final List<RecipientEntry> entries = constructEntryList(false, - entryMap, nonAggregatedEntries, existingDestinations); + final List<RecipientEntry> entries = constructEntryList( + entryMap, nonAggregatedEntries); // After having local results, check the size of results. If the results are // not enough, we search remote directories, which will take longer time. @@ -250,7 +250,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter directoryCursor = mContentResolver.query( DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null); - paramsList = setupOtherDirectories(directoryCursor); + paramsList = setupOtherDirectories(mContext, directoryCursor, mAccount); } else { // We don't need to search other directories. paramsList = null; @@ -424,8 +424,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter } // Show the list again without "waiting" message. - updateEntries(constructEntryList(false, - mEntryMap, mNonAggregatedEntries, mExistingDestinations)); + updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries)); } } @@ -482,8 +481,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter @Override public void handleMessage(Message msg) { if (mRemainingDirectoryCount > 0) { - updateEntries(constructEntryList(true, - mEntryMap, mNonAggregatedEntries, mExistingDestinations)); + updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries)); } } @@ -556,8 +554,9 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter return new DefaultFilter(); } - private List<DirectorySearchParams> setupOtherDirectories(Cursor directoryCursor) { - final PackageManager packageManager = mContext.getPackageManager(); + public static List<DirectorySearchParams> setupOtherDirectories(Context context, + Cursor directoryCursor, Account account) { + final PackageManager packageManager = context.getPackageManager(); final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>(); DirectorySearchParams preferredDirectory = null; while (directoryCursor.moveToNext()) { @@ -594,8 +593,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter // If an account has been provided and we found a directory that // corresponds to that account, place that directory second, directly // underneath the local contacts. - if (mAccount != null && mAccount.name.equals(params.accountName) && - mAccount.type.equals(params.accountType)) { + if (account != null && account.name.equals(params.accountName) && + account.type.equals(params.accountType)) { preferredDirectory = params; } else { paramsList.add(params); @@ -633,7 +632,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter mDelayedMessageHandler.sendDelayedLoadMessage(); } - private void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry, + private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry, LinkedHashMap<Long, List<RecipientEntry>> entryMap, List<RecipientEntry> nonAggregatedEntries, Set<String> existingDestinations) { @@ -648,7 +647,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true)); } else if (entryMap.containsKey(entry.contactId)) { // We already have a section for the person. final List<RecipientEntry> entryList = entryMap.get(entry.contactId); @@ -656,14 +655,14 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true)); } else { final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>(); entryList.add(RecipientEntry.constructTopLevelEntry( entry.displayName, entry.displayNameSource, entry.destination, entry.destinationType, entry.destinationLabel, - entry.contactId, entry.dataId, entry.thumbnailUriString)); + entry.contactId, entry.dataId, entry.thumbnailUriString, true)); entryMap.put(entry.contactId, entryList); } } @@ -674,10 +673,8 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter * thread to get one from directories. */ private List<RecipientEntry> constructEntryList( - boolean showMessageIfDirectoryLoadRemaining, LinkedHashMap<Long, List<RecipientEntry>> entryMap, - List<RecipientEntry> nonAggregatedEntries, - Set<String> existingDestinations) { + List<RecipientEntry> nonAggregatedEntries) { final List<RecipientEntry> entries = new ArrayList<RecipientEntry>(); int validEntryCount = 0; for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) { @@ -708,6 +705,11 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter return entries; } + + protected interface EntriesUpdatedObserver { + public void onChanged(List<RecipientEntry> entries); + } + public void registerUpdateObserver(EntriesUpdatedObserver observer) { mEntriesUpdatedObserver = observer; } @@ -906,7 +908,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter if (imageView != null) { imageView.setVisibility(View.VISIBLE); final byte[] photoBytes = entry.getPhotoBytes(); - if (photoBytes != null && imageView != null) { + if (photoBytes != null) { final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length); imageView.setImageBitmap(photo); @@ -975,11 +977,7 @@ public abstract class BaseRecipientAdapter extends BaseAdapter implements Filter return android.R.id.icon; } - /** - * Interface called before the BaseRecipientAdapter updates recipient - * results in the popup window. - */ - protected interface EntriesUpdatedObserver { - public void onChanged(List<RecipientEntry> entries); + public Account getAccount() { + return mAccount; } } diff --git a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java index 553890e..0693df2 100644 --- a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java +++ b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java @@ -16,9 +16,14 @@ package com.android.ex.chips; +import android.accounts.Account; +import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.ContactsContract; +import android.text.TextUtils; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; import android.util.Log; @@ -29,11 +34,16 @@ import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.TextView; +import com.android.ex.chips.BaseRecipientAdapter.DirectoryListQuery; +import com.android.ex.chips.BaseRecipientAdapter.DirectorySearchParams; import com.android.ex.chips.Queries.Query; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * RecipientAlternatesAdapter backs the RecipientEditTextView for managing contacts @@ -55,9 +65,17 @@ public class RecipientAlternatesAdapter extends CursorAdapter { public static final int QUERY_TYPE_PHONE = 1; private Query mQuery; - public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context, - ArrayList<String> inAddresses) { - return getMatchingRecipients(context, inAddresses, QUERY_TYPE_EMAIL); + public interface RecipientMatchCallback { + public void matchesFound(Map<String, RecipientEntry> results); + /** + * Called with all addresses that could not be resolved to valid recipients. + */ + public void matchesNotFound(Set<String> unfoundAddresses); + } + + public static void getMatchingRecipients(Context context, ArrayList<String> inAddresses, + Account account, RecipientMatchCallback callback) { + getMatchingRecipients(context, inAddresses, QUERY_TYPE_EMAIL, account, callback); } /** @@ -67,10 +85,11 @@ public class RecipientAlternatesAdapter extends CursorAdapter { * * @param context Context. * @param inAddresses Array of addresses on which to perform the lookup. + * @param callback RecipientMatchCallback called when a match or matches are found. * @return HashMap<String,RecipientEntry> */ - public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context, - ArrayList<String> inAddresses, int addressType) { + public static void getMatchingRecipients(Context context, ArrayList<String> inAddresses, + int addressType, Account account, RecipientMatchCallback callback) { Queries.Query query; if (addressType == QUERY_TYPE_EMAIL) { query = Queries.EMAIL; @@ -78,12 +97,12 @@ public class RecipientAlternatesAdapter extends CursorAdapter { query = Queries.PHONE; } int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size()); - String[] addresses = new String[addressesSize]; + HashSet<String> addresses = new HashSet<String>(); StringBuilder bindString = new StringBuilder(); // Create the "?" string and set up arguments. for (int i = 0; i < addressesSize; i++) { Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase()); - addresses[i] = (tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i)); + addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i)); bindString.append("?"); if (i < addressesSize - 1) { bindString.append(","); @@ -94,49 +113,205 @@ public class RecipientAlternatesAdapter extends CursorAdapter { Log.d(TAG, "Doing reverse lookup for " + addresses.toString()); } - HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>(); - Cursor c = context.getContentResolver().query( - query.getContentUri(), - query.getProjection(), - query.getProjection()[Queries.Query.DESTINATION] + " IN (" + bindString.toString() - + ")", addresses, null); - - if (c != null) { + String[] addressArray = new String[addresses.size()]; + addresses.toArray(addressArray); + HashMap<String, RecipientEntry> recipientEntries = null; + Cursor c = null; + + try { + c = context.getContentResolver().query( + query.getContentUri(), + query.getProjection(), + query.getProjection()[Queries.Query.DESTINATION] + " IN (" + + bindString.toString() + ")", addressArray, null); + recipientEntries = processContactEntries(c); + callback.matchesFound(recipientEntries); + } finally { + if (c != null) { + c.close(); + } + } + // See if any entries did not resolve; if so, we need to check other + // directories + final Set<String> matchesNotFound = new HashSet<String>(); + if (recipientEntries.size() < addresses.size()) { + final List<DirectorySearchParams> paramsList; + Cursor directoryCursor = null; try { - if (c.moveToFirst()) { - do { - String address = c.getString(Queries.Query.DESTINATION); - recipientEntries.put(address, RecipientEntry.constructTopLevelEntry( - c.getString(Queries.Query.NAME), - c.getInt(Queries.Query.DISPLAY_NAME_SOURCE), - c.getString(Queries.Query.DESTINATION), - c.getInt(Queries.Query.DESTINATION_TYPE), - c.getString(Queries.Query.DESTINATION_LABEL), - c.getLong(Queries.Query.CONTACT_ID), - c.getLong(Queries.Query.DATA_ID), - c.getString(Queries.Query.PHOTO_THUMBNAIL_URI))); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Received reverse look up information for " + address - + " RESULTS: " - + " NAME : " + c.getString(Queries.Query.NAME) - + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID) - + " ADDRESS :" + c.getString(Queries.Query.DESTINATION)); + directoryCursor = context.getContentResolver().query(DirectoryListQuery.URI, + DirectoryListQuery.PROJECTION, null, null, null); + paramsList = BaseRecipientAdapter.setupOtherDirectories(context, directoryCursor, + account); + } finally { + if (directoryCursor != null) { + directoryCursor.close(); + } + } + // Run a directory query for each unmatched recipient. + HashSet<String> unresolvedAddresses = new HashSet<String>(); + for (String address : addresses) { + if (!recipientEntries.containsKey(address)) { + unresolvedAddresses.add(address); + } + } + + matchesNotFound.addAll(unresolvedAddresses); + + Cursor directoryContactsCursor = null; + for (String unresolvedAddress : unresolvedAddresses) { + for (int i = 0; i < paramsList.size(); i++) { + try { + directoryContactsCursor = doQuery(unresolvedAddress, 1, + paramsList.get(i).directoryId, account, + context.getContentResolver(), query); + } finally { + if (directoryContactsCursor != null + && directoryContactsCursor.getCount() == 0) { + directoryContactsCursor.close(); + directoryContactsCursor = null; + } else { + break; } - } while (c.moveToNext()); + } + } + if (directoryContactsCursor != null) { + try { + final Map<String, RecipientEntry> entries = + processContactEntries(directoryContactsCursor); + + for (final String address : entries.keySet()) { + matchesNotFound.remove(address); + } + + callback.matchesFound(entries); + } finally { + directoryContactsCursor.close(); + } } - } finally { - c.close(); } } + + callback.matchesNotFound(matchesNotFound); + } + + private static HashMap<String, RecipientEntry> processContactEntries(Cursor c) { + HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>(); + if (c != null && c.moveToFirst()) { + do { + String address = c.getString(Queries.Query.DESTINATION); + + final RecipientEntry newRecipientEntry = RecipientEntry.constructTopLevelEntry( + c.getString(Queries.Query.NAME), + c.getInt(Queries.Query.DISPLAY_NAME_SOURCE), + c.getString(Queries.Query.DESTINATION), + c.getInt(Queries.Query.DESTINATION_TYPE), + c.getString(Queries.Query.DESTINATION_LABEL), + c.getLong(Queries.Query.CONTACT_ID), + c.getLong(Queries.Query.DATA_ID), + c.getString(Queries.Query.PHOTO_THUMBNAIL_URI), + true); + + /* + * In certain situations, we may have two results for one address, where one of the + * results is just the email address, and the other has a name and photo, so we want + * to use the better one. + */ + final RecipientEntry recipientEntry = + getBetterRecipient(recipientEntries.get(address), newRecipientEntry); + + recipientEntries.put(address, recipientEntry); + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received reverse look up information for " + address + + " RESULTS: " + + " NAME : " + c.getString(Queries.Query.NAME) + + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID) + + " ADDRESS :" + c.getString(Queries.Query.DESTINATION)); + } + } while (c.moveToNext()); + } return recipientEntries; } - public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int viewId, + /** + * Given two {@link RecipientEntry}s for the same email address, this will return the one that + * contains more complete information for display purposes. Defaults to <code>entry2</code> if + * no significant differences are found. + */ + static RecipientEntry getBetterRecipient(final RecipientEntry entry1, + final RecipientEntry entry2) { + // If only one has passed in, use it + if (entry2 == null) { + return entry1; + } + + if (entry1 == null) { + return entry2; + } + + // If only one has a display name, use it + if (!TextUtils.isEmpty(entry1.getDisplayName()) + && TextUtils.isEmpty(entry2.getDisplayName())) { + return entry1; + } + + if (!TextUtils.isEmpty(entry2.getDisplayName()) + && TextUtils.isEmpty(entry1.getDisplayName())) { + return entry2; + } + + // If only one has a display name that is not the same as the destination, use it + if (!TextUtils.equals(entry1.getDisplayName(), entry1.getDestination()) + && TextUtils.equals(entry2.getDisplayName(), entry2.getDestination())) { + return entry1; + } + + if (!TextUtils.equals(entry2.getDisplayName(), entry2.getDestination()) + && TextUtils.equals(entry1.getDisplayName(), entry1.getDestination())) { + return entry2; + } + + // If only one has a photo, use it + if ((entry1.getPhotoThumbnailUri() != null || entry1.getPhotoBytes() != null) + && (entry2.getPhotoThumbnailUri() == null && entry2.getPhotoBytes() == null)) { + return entry1; + } + + if ((entry2.getPhotoThumbnailUri() != null || entry2.getPhotoBytes() != null) + && (entry1.getPhotoThumbnailUri() == null && entry1.getPhotoBytes() == null)) { + return entry2; + } + + // Go with the second option as a default + return entry2; + } + + private static Cursor doQuery(CharSequence constraint, int limit, Long directoryId, + Account account, ContentResolver resolver, Query query) { + final Uri.Builder builder = query + .getContentFilterUri() + .buildUpon() + .appendPath(constraint.toString()) + .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, + String.valueOf(limit + BaseRecipientAdapter.ALLOWANCE_FOR_DUPLICATES)); + if (directoryId != null) { + builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, + String.valueOf(directoryId)); + } + if (account != null) { + builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_NAME, account.name); + builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_TYPE, account.type); + } + final Cursor cursor = resolver.query(builder.build(), query.getProjection(), null, null, + null); + return cursor; + } + + public RecipientAlternatesAdapter(Context context, long contactId, long currentId, OnCheckedItemChangedListener listener) { - this(context, contactId, currentId, viewId, QUERY_TYPE_EMAIL, listener); + this(context, contactId, currentId, QUERY_TYPE_EMAIL, listener); } - public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int viewId, + public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int queryMode, OnCheckedItemChangedListener listener) { super(context, getCursorForConstruction(context, contactId, queryMode), 0); mLayoutInflater = LayoutInflater.from(context); @@ -233,7 +408,8 @@ public class RecipientAlternatesAdapter extends CursorAdapter { c.getString(Queries.Query.DESTINATION_LABEL), c.getLong(Queries.Query.CONTACT_ID), c.getLong(Queries.Query.DATA_ID), - c.getString(Queries.Query.PHOTO_THUMBNAIL_URI)); + c.getString(Queries.Query.PHOTO_THUMBNAIL_URI), + true); } @Override diff --git a/chips/src/com/android/ex/chips/RecipientEditTextView.java b/chips/src/com/android/ex/chips/RecipientEditTextView.java index 93ca4e8..d2e5806 100644 --- a/chips/src/com/android/ex/chips/RecipientEditTextView.java +++ b/chips/src/com/android/ex/chips/RecipientEditTextView.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +17,18 @@ package com.android.ex.chips; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import android.app.Dialog; import android.content.ClipData; import android.content.ClipDescription; @@ -36,6 +49,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Handler; +import android.os.Looper; import android.os.Message; import android.os.Parcelable; import android.text.Editable; @@ -55,7 +69,6 @@ import android.text.util.Rfc822Tokenizer; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; -import android.util.Patterns; import android.view.ActionMode; import android.view.ActionMode.Callback; import android.view.DragEvent; @@ -81,17 +94,10 @@ import android.widget.MultiAutoCompleteTextView; import android.widget.ScrollView; import android.widget.TextView; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback; +import com.android.ex.chips.recipientchip.DrawableRecipientChip; +import com.android.ex.chips.recipientchip.InvisibleRecipientChip; +import com.android.ex.chips.recipientchip.VisibleRecipientChip; /** * RecipientEditTextView is an auto complete text view for use with applications @@ -104,12 +110,13 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements private static final char COMMIT_CHAR_COMMA = ','; - private static final char NAME_WRAPPER_CHAR = '"'; - private static final char COMMIT_CHAR_SEMICOLON = ';'; private static final char COMMIT_CHAR_SPACE = ' '; + private static final String SEPARATOR = String.valueOf(COMMIT_CHAR_COMMA) + + String.valueOf(COMMIT_CHAR_SPACE); + private static final String TAG = "RecipientEditTextView"; private static int DISMISS = "dismiss".hashCode(); @@ -145,7 +152,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements private Validator mValidator; - private RecipientChip mSelectedChip; + private DrawableRecipientChip mSelectedChip; private int mAlternatesLayout; @@ -155,7 +162,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements private TextView mMoreItem; - private final ArrayList<String> mPendingChips = new ArrayList<String>(); + // VisibleForTesting + final ArrayList<String> mPendingChips = new ArrayList<String>(); private Handler mHandler; @@ -167,9 +175,10 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements private ListPopupWindow mAddressPopup; - private ArrayList<RecipientChip> mTemporaryRecipients; + // VisibleForTesting + ArrayList<DrawableRecipientChip> mTemporaryRecipients; - private ArrayList<RecipientChip> mRemovedSpans; + private ArrayList<DrawableRecipientChip> mRemovedSpans; private boolean mShouldShrink = true; @@ -206,7 +215,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements + "(1?[ ]*\\([0-9]+\\)[\\- \\.]*)?" // 1(<digits>)<sdd>* + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit> - private final Runnable mAddTextWatcher = new Runnable() { @Override public void run() { @@ -282,7 +290,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements addTextChangedListener(mTextWatcher); mGestureDetector = new GestureDetector(context, this); setOnEditorActionListener(this); - mMaxLines = getLineCount(); } @Override @@ -314,13 +321,15 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION; } + + outAttrs.actionId = EditorInfo.IME_ACTION_DONE; outAttrs.actionLabel = getContext().getString(R.string.done); return connection; } - /*package*/ RecipientChip getLastChip() { - RecipientChip last = null; - RecipientChip[] chips = getSortedRecipients(); + /*package*/ DrawableRecipientChip getLastChip() { + DrawableRecipientChip last = null; + DrawableRecipientChip[] chips = getSortedRecipients(); if (chips != null && chips.length > 0) { last = chips[chips.length - 1]; } @@ -331,7 +340,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements public void onSelectionChanged(int start, int end) { // When selection changes, see if it is inside the chips area. // If so, move the cursor back after the chips again. - RecipientChip last = getLastChip(); + DrawableRecipientChip last = getLastChip(); if (last != null && start < getSpannable().getSpanEnd(last)) { // Grab the last chip and set the cursor to after it. setSelection(Math.min(getSpannable().getSpanEnd(last) + 1, getText().length())); @@ -370,22 +379,17 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements super.append(text, start, end); if (!TextUtils.isEmpty(text) && TextUtils.getTrimmedLength(text) > 0) { String displayString = text.toString(); - int separatorPos = displayString.lastIndexOf(COMMIT_CHAR_COMMA); - // Verify that the separator pos is not within ""; if it is, look - // past the closing quote. If there is no comma past ", this string - // will resolve to an error chip. - if (separatorPos > -1) { - String parseDisplayString = displayString.substring(separatorPos); - int endQuotedTextPos = parseDisplayString.indexOf(NAME_WRAPPER_CHAR); - if (endQuotedTextPos > separatorPos) { - separatorPos = parseDisplayString.lastIndexOf(COMMIT_CHAR_COMMA, - endQuotedTextPos); - } + + if (!displayString.trim().endsWith(String.valueOf(COMMIT_CHAR_COMMA))) { + // We have no separator, so we should add it + super.append(SEPARATOR, 0, SEPARATOR.length()); + displayString += SEPARATOR; } + if (!TextUtils.isEmpty(displayString) && TextUtils.getTrimmedLength(displayString) > 0) { mPendingChipsCount++; - mPendingChips.add(text.toString()); + mPendingChips.add(displayString); } } // Put a message on the queue to make sure we ALWAYS handle pending @@ -413,6 +417,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return sExcessTopPadding; } + @Override public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { super.setAdapter(adapter); ((BaseRecipientAdapter) adapter) @@ -476,7 +481,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements Editable editable = getText(); int end = getSelectionEnd(); int start = mTokenizer.findTokenStart(editable, end); - RecipientChip[] chips = getSpannable().getSpans(start, end, RecipientChip.class); + DrawableRecipientChip[] chips = + getSpannable().getSpans(start, end, DrawableRecipientChip.class); if ((chips == null || chips.length == 0)) { Editable text = getText(); int whatEnd = mTokenizer.findTokenEnd(text, start); @@ -524,7 +530,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements TextUtils.TruncateAt.END); } - private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint, Layout layout) { + private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint) { // Ellipsize the text so that it takes AT MOST the entire width of the // autocomplete text entry area. Make sure to leave space for padding // on the sides. @@ -533,7 +539,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements float[] widths = new float[1]; paint.getTextWidths(" ", widths); CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint, - calculateAvailableWidth(true) - deleteWidth - widths[0]); + calculateAvailableWidth() - deleteWidth - widths[0]); // Make sure there is a minimum chip width so the user can ALWAYS // tap a chip without difficulty. @@ -566,7 +572,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } - private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint, Layout layout, + private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint, boolean leaveBlankIconSpacer) { // Ellipsize the text so that it takes AT MOST the entire width of the // autocomplete text entry area. Make sure to leave space for padding @@ -576,7 +582,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements float[] widths = new float[1]; paint.getTextWidths(" ", widths); CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint, - calculateAvailableWidth(false) - iconWidth - widths[0]); + calculateAvailableWidth() - iconWidth - widths[0]); // Make sure there is a minimum chip width so the user can ALWAYS // tap a chip without difficulty. int width = Math.max(iconWidth * 2, (int) Math.floor(paint.measureText(ellipsizedText, 0, @@ -646,25 +652,23 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * Get the background drawable for a RecipientChip. */ // Visible for testing. - /*package*/ Drawable getChipBackground(RecipientEntry contact) { - return (mValidator != null && mValidator.isValid(contact.getDestination())) ? - mChipBackground : mInvalidChipBackground; + /* package */Drawable getChipBackground(RecipientEntry contact) { + return contact.isValid() ? mChipBackground : mInvalidChipBackground; } - private float getTextYOffset(String text, TextPaint paint, int height) { + private static float getTextYOffset(String text, TextPaint paint, int height) { Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); int textHeight = bounds.bottom - bounds.top ; return height - ((height - textHeight) / 2) - (int)paint.descent(); } - private RecipientChip constructChipSpan(RecipientEntry contact, int offset, boolean pressed, + private DrawableRecipientChip constructChipSpan(RecipientEntry contact, boolean pressed, boolean leaveIconSpace) throws NullPointerException { if (mChipBackground == null) { throw new NullPointerException( "Unable to render any chips as setChipDimensions was not called."); } - Layout layout = getLayout(); TextPaint paint = getPaint(); float defaultSize = paint.getTextSize(); @@ -672,16 +676,16 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements Bitmap tmpBitmap; if (pressed) { - tmpBitmap = createSelectedChip(contact, paint, layout); + tmpBitmap = createSelectedChip(contact, paint); } else { - tmpBitmap = createUnselectedChip(contact, paint, layout, leaveIconSpace); + tmpBitmap = createUnselectedChip(contact, paint, leaveIconSpace); } // Pass the full text, un-ellipsized, to the chip. Drawable result = new BitmapDrawable(getResources(), tmpBitmap); result.setBounds(0, 0, tmpBitmap.getWidth(), tmpBitmap.getHeight()); - RecipientChip recipientChip = new RecipientChip(result, contact, offset); + DrawableRecipientChip recipientChip = new VisibleRecipientChip(result, contact); // Return text to the original size. paint.setTextSize(defaultSize); paint.setColor(defaultColor); @@ -706,7 +710,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * account the width of the EditTextView, any view padding, and padding * that will be added to the chip. */ - private float calculateAvailableWidth(boolean pressed) { + private float calculateAvailableWidth() { return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2); } @@ -715,6 +719,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecipientEditTextView, 0, 0); Resources r = getContext().getResources(); + mChipBackground = a.getDrawable(R.styleable.RecipientEditTextView_chipBackground); if (mChipBackground == null) { mChipBackground = r.getDrawable(R.drawable.chip_background); @@ -755,7 +760,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (mInvalidChipBackground == null) { mInvalidChipBackground = r.getDrawable(R.drawable.chip_background_invalid); } - mLineSpacingExtra = context.getResources().getDimension(R.dimen.line_spacing_extra); + mLineSpacingExtra = r.getDimension(R.dimen.line_spacing_extra); + mMaxLines = r.getInteger(R.integer.chips_max_lines); TypedValue tv = new TypedValue(); if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) { mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources() @@ -821,11 +827,11 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements private void checkChipWidths() { // Check the widths of the associated chips. - RecipientChip[] chips = getSortedRecipients(); + DrawableRecipientChip[] chips = getSortedRecipients(); if (chips != null) { Rect bounds; - for (RecipientChip chip : chips) { - bounds = chip.getDrawable().getBounds(); + for (DrawableRecipientChip chip : chips) { + bounds = chip.getBounds(); if (getWidth() > 0 && bounds.right - bounds.left > getWidth()) { // Need to redraw that chip. replaceChip(chip, chip.getEntry()); @@ -853,7 +859,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements for (int i = 0; i < mPendingChips.size(); i++) { String current = mPendingChips.get(i); int tokenStart = editable.toString().indexOf(current); - int tokenEnd = tokenStart + current.length(); + // Always leave a space at the end between tokens. + int tokenEnd = tokenStart + current.length() - 1; if (tokenStart >= 0) { // When we have a valid token, include it with the token // to the left. @@ -861,7 +868,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements && editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) { tokenEnd++; } - createReplacementChip(tokenStart, tokenEnd, editable); + createReplacementChip(tokenStart, tokenEnd, editable, i < CHIP_LIMIT + || !mShouldShrink); } mPendingChipsCount--; } @@ -878,10 +886,10 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } else { // Create the "more" chip mIndividualReplacements = new IndividualReplacementTask(); - mIndividualReplacements.execute(new ArrayList<RecipientChip>( + mIndividualReplacements.execute(new ArrayList<DrawableRecipientChip>( mTemporaryRecipients.subList(0, CHIP_LIMIT))); if (mTemporaryRecipients.size() > CHIP_LIMIT) { - mTemporaryRecipients = new ArrayList<RecipientChip>( + mTemporaryRecipients = new ArrayList<DrawableRecipientChip>( mTemporaryRecipients.subList(CHIP_LIMIT, mTemporaryRecipients.size())); } else { @@ -915,17 +923,16 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return; } // Find the last chip; eliminate any commit characters after it. - RecipientChip[] chips = getSortedRecipients(); + DrawableRecipientChip[] chips = getSortedRecipients(); + Spannable spannable = getSpannable(); if (chips != null && chips.length > 0) { int end; - ImageSpan lastSpan; mMoreChip = getMoreChip(); if (mMoreChip != null) { - lastSpan = mMoreChip; + end = spannable.getSpanEnd(mMoreChip); } else { - lastSpan = getLastChip(); + end = getSpannable().getSpanEnd(getLastChip()); } - end = getSpannable().getSpanEnd(lastSpan); Editable editable = getText(); int length = editable.length(); if (length > end) { @@ -943,34 +950,35 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * Create a chip that represents just the email address of a recipient. At some later * point, this chip will be attached to a real contact entry, if one exists. */ - private void createReplacementChip(int tokenStart, int tokenEnd, Editable editable) { + // VisibleForTesting + void createReplacementChip(int tokenStart, int tokenEnd, Editable editable, + boolean visible) { if (alreadyHasChip(tokenStart, tokenEnd)) { // There is already a chip present at this location. // Don't recreate it. return; } String token = editable.toString().substring(tokenStart, tokenEnd); - int commitCharIndex = token.trim().lastIndexOf(COMMIT_CHAR_COMMA); - if (commitCharIndex == token.length() - 1) { - token = token.substring(0, token.length() - 1); + final String trimmedToken = token.trim(); + int commitCharIndex = trimmedToken.lastIndexOf(COMMIT_CHAR_COMMA); + if (commitCharIndex != -1 && commitCharIndex == trimmedToken.length() - 1) { + token = trimmedToken.substring(0, trimmedToken.length() - 1); } RecipientEntry entry = createTokenizedEntry(token); if (entry != null) { - String destText = createAddressText(entry); - SpannableString chipText = new SpannableString(destText); - int end = getSelectionEnd(); - int start = mTokenizer != null ? mTokenizer.findTokenStart(getText(), end) : 0; - RecipientChip chip = null; + DrawableRecipientChip chip = null; try { if (!mNoChips) { - /* leave space for the contact icon if this is not just an email address */ - chip = constructChipSpan( - entry, - start, - false, - TextUtils.isEmpty(entry.getDisplayName()) - || TextUtils.equals(entry.getDisplayName(), - entry.getDestination())); + /* + * leave space for the contact icon if this is not just an + * email address + */ + boolean leaveSpace = TextUtils.isEmpty(entry.getDisplayName()) + || TextUtils.equals(entry.getDisplayName(), + entry.getDestination()); + chip = visible ? + constructChipSpan(entry, false, leaveSpace) + : new InvisibleRecipientChip(entry); } } catch (NullPointerException e) { Log.e(TAG, e.getMessage(), e); @@ -979,9 +987,9 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements // Add this chip to the list of entries "to replace" if (chip != null) { if (mTemporaryRecipients == null) { - mTemporaryRecipients = new ArrayList<RecipientChip>(); + mTemporaryRecipients = new ArrayList<DrawableRecipientChip>(); } - chip.setOriginalText(chipText.toString()); + chip.setOriginalText(token); mTemporaryRecipients.add(chip); } } @@ -999,70 +1007,68 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return match.matches(); } - private RecipientEntry createTokenizedEntry(String token) { + // VisibleForTesting + RecipientEntry createTokenizedEntry(final String token) { if (TextUtils.isEmpty(token)) { return null; } if (isPhoneQuery() && isPhoneNumber(token)) { - return RecipientEntry - .constructFakeEntry(token); + return RecipientEntry.constructFakePhoneEntry(token, true); } Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(token); String display = null; - if (isValid(token) && tokens != null && tokens.length > 0) { + boolean isValid = isValid(token); + if (isValid && tokens != null && tokens.length > 0) { // If we can get a name from tokenizing, then generate an entry from // this. display = tokens[0].getName(); if (!TextUtils.isEmpty(display)) { - if (!isPhoneQuery()) { - if (!TextUtils.isEmpty(token)) { - token = token.trim(); - } - char charAt = token.charAt(token.length() - 1); - if (charAt == COMMIT_CHAR_COMMA || charAt == COMMIT_CHAR_SEMICOLON) { - token = token.substring(0, token.length() - 1); - } - } - return RecipientEntry.constructGeneratedEntry(display, token); + return RecipientEntry.constructGeneratedEntry(display, tokens[0].getAddress(), + isValid); } else { display = tokens[0].getAddress(); if (!TextUtils.isEmpty(display)) { - return RecipientEntry.constructFakeEntry(display); + return RecipientEntry.constructFakeEntry(display, isValid); } } } // Unable to validate the token or to create a valid token from it. // Just create a chip the user can edit. String validatedToken = null; - if (mValidator != null && !mValidator.isValid(token)) { + if (mValidator != null && !isValid) { // Try fixing up the entry using the validator. validatedToken = mValidator.fixText(token).toString(); if (!TextUtils.isEmpty(validatedToken)) { if (validatedToken.contains(token)) { - // protect against the case of a validator with a null domain, + // protect against the case of a validator with a null + // domain, // which doesn't add a domain to the token Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(validatedToken); if (tokenized.length > 0) { validatedToken = tokenized[0].getAddress(); + isValid = true; } } else { - // We ran into a case where the token was invalid and removed - // by the validator. In this case, just use the original token + // We ran into a case where the token was invalid and + // removed + // by the validator. In this case, just use the original + // token // and let the user sort out the error chip. validatedToken = null; + isValid = false; } } } // Otherwise, fallback to just creating an editable email address chip. - return RecipientEntry - .constructFakeEntry(!TextUtils.isEmpty(validatedToken) ? validatedToken : token); + return RecipientEntry.constructFakeEntry( + !TextUtils.isEmpty(validatedToken) ? validatedToken : token, isValid); } private boolean isValid(String text) { return mValidator == null ? true : mValidator.isValid(text); } - private String tokenizeAddress(String destination) { + private static String tokenizeAddress(String destination) { Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(destination); if (tokens != null && tokens.length > 0) { return tokens[0].getAddress(); @@ -1115,20 +1121,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - if (event.hasNoModifiers()) { - if (commitDefault()) { - return true; - } - if (mSelectedChip != null) { - clearSelectedChip(); - return true; - } else if (focusNext()) { - return true; - } - } - break; case KeyEvent.KEYCODE_TAB: if (event.hasNoModifiers()) { if (mSelectedChip != null) { @@ -1136,9 +1128,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } else { commitDefault(); } - if (focusNext()) { - return true; - } } break; } @@ -1246,10 +1235,10 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return; } // Find the last chip. - RecipientChip[] recips = getSortedRecipients(); + DrawableRecipientChip[] recips = getSortedRecipients(); if (recips != null && recips.length > 0) { - RecipientChip last = recips[recips.length - 1]; - RecipientChip beforeLast = null; + DrawableRecipientChip last = recips[recips.length - 1]; + DrawableRecipientChip beforeLast = null; if (recips.length > 1) { beforeLast = recips[recips.length - 2]; } @@ -1280,7 +1269,8 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (mNoChips) { return true; } - RecipientChip[] chips = getSpannable().getSpans(start, end, RecipientChip.class); + DrawableRecipientChip[] chips = + getSpannable().getSpans(start, end, DrawableRecipientChip.class); if ((chips == null || chips.length == 0)) { return false; } @@ -1299,7 +1289,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements setSelection(end); String text = getText().toString().substring(start, end); if (!TextUtils.isEmpty(text)) { - RecipientEntry entry = RecipientEntry.constructFakeEntry(text); + RecipientEntry entry = RecipientEntry.constructFakeEntry(text, isValid(text)); QwertyKeyListener.markAsReplaced(editable, start, end, ""); CharSequence chipText = createChip(entry, false); int selEnd = getSelectionEnd(); @@ -1323,8 +1313,21 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements removeChip(mSelectedChip); } - if (keyCode == KeyEvent.KEYCODE_ENTER && event.hasNoModifiers()) { - return true; + switch (keyCode) { + case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_DPAD_CENTER: + if (event.hasNoModifiers()) { + if (commitDefault()) { + return true; + } + if (mSelectedChip != null) { + clearSelectedChip(); + return true; + } else if (focusNext()) { + return true; + } + } + break; } return super.onKeyDown(keyCode, event); @@ -1335,11 +1338,11 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return getText(); } - private int getChipStart(RecipientChip chip) { + private int getChipStart(DrawableRecipientChip chip) { return getSpannable().getSpanStart(chip); } - private int getChipEnd(RecipientChip chip) { + private int getChipEnd(DrawableRecipientChip chip) { return getSpannable().getSpanEnd(chip); } @@ -1359,7 +1362,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements // If this is a RecipientChip, don't filter // on its contents. Spannable span = getSpannable(); - RecipientChip[] chips = span.getSpans(start, end, RecipientChip.class); + DrawableRecipientChip[] chips = span.getSpans(start, end, DrawableRecipientChip.class); if (chips != null && chips.length > 0) { return; } @@ -1417,7 +1420,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements float x = event.getX(); float y = event.getY(); int offset = putOffsetInRange(getOffsetForPosition(x, y)); - RecipientChip currentChip = findChip(offset); + DrawableRecipientChip currentChip = findChip(offset); if (currentChip != null) { if (action == MotionEvent.ACTION_UP) { if (mSelectedChip != null && mSelectedChip != currentChip) { @@ -1449,44 +1452,54 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } } - private void showAlternates(RecipientChip currentChip, ListPopupWindow alternatesPopup, - int width, Context context) { - int line = getLayout().getLineForOffset(getChipStart(currentChip)); - int bottom; - if (line == getLineCount() -1) { - bottom = 0; - } else { - bottom = -(int) ((mChipHeight + (2 * mLineSpacingExtra)) * (Math.abs(getLineCount() - 1 - - line))); - } - // Align the alternates popup with the left side of the View, - // regardless of the position of the chip tapped. - alternatesPopup.setWidth(width); - alternatesPopup.setAnchorView(this); - alternatesPopup.setVerticalOffset(bottom); - alternatesPopup.setAdapter(createAlternatesAdapter(currentChip)); - alternatesPopup.setOnItemClickListener(mAlternatesListener); - // Clear the checked item. - mCheckedItem = -1; - alternatesPopup.show(); - ListView listView = alternatesPopup.getListView(); - listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - // Checked item would be -1 if the adapter has not - // loaded the view that should be checked yet. The - // variable will be set correctly when onCheckedItemChanged - // is called in a separate thread. - if (mCheckedItem != -1) { - listView.setItemChecked(mCheckedItem, true); - mCheckedItem = -1; - } + private void showAlternates(final DrawableRecipientChip currentChip, + final ListPopupWindow alternatesPopup, final int width) { + new AsyncTask<Void, Void, ListAdapter>() { + @Override + protected ListAdapter doInBackground(final Void... params) { + return createAlternatesAdapter(currentChip); + } + + @Override + protected void onPostExecute(final ListAdapter result) { + int line = getLayout().getLineForOffset(getChipStart(currentChip)); + int bottom; + if (line == getLineCount() -1) { + bottom = 0; + } else { + bottom = -(int) ((mChipHeight + (2 * mLineSpacingExtra)) * (Math + .abs(getLineCount() - 1 - line))); + } + // Align the alternates popup with the left side of the View, + // regardless of the position of the chip tapped. + alternatesPopup.setWidth(width); + alternatesPopup.setAnchorView(RecipientEditTextView.this); + alternatesPopup.setVerticalOffset(bottom); + alternatesPopup.setAdapter(result); + alternatesPopup.setOnItemClickListener(mAlternatesListener); + // Clear the checked item. + mCheckedItem = -1; + alternatesPopup.show(); + ListView listView = alternatesPopup.getListView(); + listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + // Checked item would be -1 if the adapter has not + // loaded the view that should be checked yet. The + // variable will be set correctly when onCheckedItemChanged + // is called in a separate thread. + if (mCheckedItem != -1) { + listView.setItemChecked(mCheckedItem, true); + mCheckedItem = -1; + } + } + }.execute((Void[]) null); } - private ListAdapter createAlternatesAdapter(RecipientChip chip) { + private ListAdapter createAlternatesAdapter(DrawableRecipientChip chip) { return new RecipientAlternatesAdapter(getContext(), chip.getContactId(), chip.getDataId(), - mAlternatesLayout, ((BaseRecipientAdapter)getAdapter()).getQueryType(), this); + ((BaseRecipientAdapter)getAdapter()).getQueryType(), this); } - private ListAdapter createSingleAddressAdapter(RecipientChip currentChip) { + private ListAdapter createSingleAddressAdapter(DrawableRecipientChip currentChip) { return new SingleRecipientArrayAdapter(getContext(), mAlternatesLayout, currentChip .getEntry()); } @@ -1530,18 +1543,19 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return offset; } - private int findText(Editable text, int offset) { + private static int findText(Editable text, int offset) { if (text.charAt(offset) != ' ') { return offset; } return -1; } - private RecipientChip findChip(int offset) { - RecipientChip[] chips = getSpannable().getSpans(0, getText().length(), RecipientChip.class); + private DrawableRecipientChip findChip(int offset) { + DrawableRecipientChip[] chips = + getSpannable().getSpans(0, getText().length(), DrawableRecipientChip.class); // Find the chip that contains this offset. for (int i = 0; i < chips.length; i++) { - RecipientChip chip = chips[i]; + DrawableRecipientChip chip = chips[i]; int start = getChipStart(chip); int end = getChipEnd(chip); if (offset >= start && offset <= end) { @@ -1588,14 +1602,6 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) { display = null; } - if (address != null && !(isPhoneQuery() && isPhoneNumber(address))) { - // Tokenize out the address in case the address already - // contained the username as well. - Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address); - if (tokenized != null && tokenized.length > 0) { - address = tokenized[0].getAddress(); - } - } if (!TextUtils.isEmpty(display)) { return display; } else if (!TextUtils.isEmpty(address)){ @@ -1612,13 +1618,11 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } SpannableString chipText = null; // Always leave a blank space at the end of a chip. - int end = getSelectionEnd(); - int start = mTokenizer.findTokenStart(getText(), end); - int textLength = displayText.length()-1; + int textLength = displayText.length() - 1; chipText = new SpannableString(displayText); if (!mNoChips) { try { - RecipientChip chip = constructChipSpan(entry, start, pressed, + DrawableRecipientChip chip = constructChipSpan(entry, pressed, false /* leave space for contact icon */); chipText.setSpan(chip, 0, textLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -1637,6 +1641,9 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements */ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (position < 0) { + return; + } submitItemAtPosition(position); } @@ -1671,12 +1678,12 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements String destination = item.getDestination(); if (!isPhoneQuery() && item.getContactId() == RecipientEntry.GENERATED_CONTACT) { entry = RecipientEntry.constructGeneratedEntry(item.getDisplayName(), - destination); + destination, item.isValid()); } else if (RecipientEntry.isCreatedRecipient(item.getContactId()) && (TextUtils.isEmpty(item.getDisplayName()) || TextUtils.equals(item.getDisplayName(), destination) || (mValidator != null && !mValidator.isValid(destination)))) { - entry = RecipientEntry.constructFakeEntry(destination); + entry = RecipientEntry.constructFakeEntry(destination, item.isValid()); } else { entry = item; } @@ -1686,9 +1693,9 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements /** Returns a collection of contact Id for each chip inside this View. */ /* package */ Collection<Long> getContactIds() { final Set<Long> result = new HashSet<Long>(); - RecipientChip[] chips = getSortedRecipients(); + DrawableRecipientChip[] chips = getSortedRecipients(); if (chips != null) { - for (RecipientChip chip : chips) { + for (DrawableRecipientChip chip : chips) { result.add(chip.getContactId()); } } @@ -1699,9 +1706,9 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements /** Returns a collection of data Id for each chip inside this View. May be null. */ /* package */ Collection<Long> getDataIds() { final Set<Long> result = new HashSet<Long>(); - RecipientChip [] chips = getSortedRecipients(); + DrawableRecipientChip [] chips = getSortedRecipients(); if (chips != null) { - for (RecipientChip chip : chips) { + for (DrawableRecipientChip chip : chips) { result.add(chip.getDataId()); } } @@ -1709,16 +1716,16 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } // Visible for testing. - /* package */RecipientChip[] getSortedRecipients() { - RecipientChip[] recips = getSpannable() - .getSpans(0, getText().length(), RecipientChip.class); - ArrayList<RecipientChip> recipientsList = new ArrayList<RecipientChip>(Arrays - .asList(recips)); + /* package */DrawableRecipientChip[] getSortedRecipients() { + DrawableRecipientChip[] recips = getSpannable() + .getSpans(0, getText().length(), DrawableRecipientChip.class); + ArrayList<DrawableRecipientChip> recipientsList = new ArrayList<DrawableRecipientChip>( + Arrays.asList(recips)); final Spannable spannable = getSpannable(); - Collections.sort(recipientsList, new Comparator<RecipientChip>() { + Collections.sort(recipientsList, new Comparator<DrawableRecipientChip>() { @Override - public int compare(RecipientChip first, RecipientChip second) { + public int compare(DrawableRecipientChip first, DrawableRecipientChip second) { int firstStart = spannable.getSpanStart(first); int secondStart = spannable.getSpanStart(second); if (firstStart < secondStart) { @@ -1730,7 +1737,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } } }); - return recipientsList.toArray(new RecipientChip[recipientsList.size()]); + return recipientsList.toArray(new DrawableRecipientChip[recipientsList.size()]); } @Override @@ -1837,7 +1844,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (tempMore.length > 0) { getSpannable().removeSpan(tempMore[0]); } - RecipientChip[] recipients = getSortedRecipients(); + DrawableRecipientChip[] recipients = getSortedRecipients(); if (recipients == null || recipients.length <= CHIP_LIMIT) { mMoreChip = null; @@ -1847,7 +1854,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements int numRecipients = recipients.length; int overage = numRecipients - CHIP_LIMIT; MoreImageSpan moreSpan = createMoreSpan(overage); - mRemovedSpans = new ArrayList<RecipientChip>(); + mRemovedSpans = new ArrayList<DrawableRecipientChip>(); int totalReplaceStart = 0; int totalReplaceEnd = 0; Editable text = getText(); @@ -1894,7 +1901,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements // Re-add the spans that were removed. if (mRemovedSpans != null && mRemovedSpans.size() > 0) { // Recreate each removed span. - RecipientChip[] recipients = getSortedRecipients(); + DrawableRecipientChip[] recipients = getSortedRecipients(); // Start the search for tokens after the last currently visible // chip. if (recipients == null || recipients.length == 0) { @@ -1902,7 +1909,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } int end = span.getSpanEnd(recipients[recipients.length - 1]); Editable editable = getText(); - for (RecipientChip chip : mRemovedSpans) { + for (DrawableRecipientChip chip : mRemovedSpans) { int chipStart; int chipEnd; String token; @@ -1937,7 +1944,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * @return A RecipientChip in the selected state or null if the chip * just contained an email address. */ - private RecipientChip selectChip(RecipientChip currentChip) { + private DrawableRecipientChip selectChip(DrawableRecipientChip currentChip) { if (shouldShowEditableText(currentChip)) { CharSequence text = currentChip.getValue(); Editable editable = getText(); @@ -1950,18 +1957,18 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements setSelection(editable.length()); editable.append(text); return constructChipSpan( - RecipientEntry.constructFakeEntry((String) text), - getSelectionStart(), true, false); + RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())), + true, false); } else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT) { int start = getChipStart(currentChip); int end = getChipEnd(currentChip); getSpannable().removeSpan(currentChip); - RecipientChip newChip; + DrawableRecipientChip newChip; try { if (mNoChips) { return null; } - newChip = constructChipSpan(currentChip.getEntry(), start, true, false); + newChip = constructChipSpan(currentChip.getEntry(), true, false); } catch (NullPointerException e) { Log.e(TAG, e.getMessage(), e); return null; @@ -1977,16 +1984,16 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (shouldShowEditableText(newChip)) { scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip))); } - showAddress(newChip, mAddressPopup, getWidth(), getContext()); + showAddress(newChip, mAddressPopup, getWidth()); setCursorVisible(false); return newChip; } else { int start = getChipStart(currentChip); int end = getChipEnd(currentChip); getSpannable().removeSpan(currentChip); - RecipientChip newChip; + DrawableRecipientChip newChip; try { - newChip = constructChipSpan(currentChip.getEntry(), start, true, false); + newChip = constructChipSpan(currentChip.getEntry(), true, false); } catch (NullPointerException e) { Log.e(TAG, e.getMessage(), e); return null; @@ -2002,20 +2009,20 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (shouldShowEditableText(newChip)) { scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip))); } - showAlternates(newChip, mAlternatesPopup, getWidth(), getContext()); + showAlternates(newChip, mAlternatesPopup, getWidth()); setCursorVisible(false); return newChip; } } - private boolean shouldShowEditableText(RecipientChip currentChip) { + private boolean shouldShowEditableText(DrawableRecipientChip currentChip) { long contactId = currentChip.getContactId(); return contactId == RecipientEntry.INVALID_CONTACT || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT); } - private void showAddress(final RecipientChip currentChip, final ListPopupWindow popup, - int width, Context context) { + private void showAddress(final DrawableRecipientChip currentChip, final ListPopupWindow popup, + int width) { int line = getLayout().getLineForOffset(getChipStart(currentChip)); int bottom = calculateOffsetFromBottom(line); // Align the alternates popup with the left side of the View, @@ -2042,7 +2049,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * the chip without a delete icon and with an unfocused background. This is * called when the RecipientChip no longer has focus. */ - private void unselectChip(RecipientChip chip) { + private void unselectChip(DrawableRecipientChip chip) { int start = getChipStart(chip); int end = getChipEnd(chip); Editable editable = getText(); @@ -2057,7 +2064,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements editable.removeSpan(chip); try { if (!mNoChips) { - editable.setSpan(constructChipSpan(chip.getEntry(), start, false, false), + editable.setSpan(constructChipSpan(chip.getEntry(), false, false), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } catch (NullPointerException e) { @@ -2080,9 +2087,10 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * right after the selected chip. * @return boolean */ - private boolean isInDelete(RecipientChip chip, int offset, float x, float y) { + private boolean isInDelete(DrawableRecipientChip chip, int offset, float x, float y) { // Figure out the bounds of this chip and whether or not // the user clicked in the X portion. + // TODO: Should x and y be used, or removed? return chip.isSelected() && offset == getChipEnd(chip); } @@ -2090,7 +2098,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * Remove the chip and any text associated with it from the RecipientEditTextView. */ // Visible for testing. - /*pacakge*/ void removeChip(RecipientChip chip) { + /*pacakge*/ void removeChip(DrawableRecipientChip chip) { Spannable spannable = getSpannable(); int spanStart = spannable.getSpanStart(chip); int spanEnd = spannable.getSpanEnd(chip); @@ -2119,7 +2127,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * that uses the contact data provided. */ // Visible for testing. - /*package*/ void replaceChip(RecipientChip chip, RecipientEntry entry) { + /*package*/ void replaceChip(DrawableRecipientChip chip, RecipientEntry entry) { boolean wasSelected = chip == mSelectedChip; if (wasSelected) { mSelectedChip = null; @@ -2157,7 +2165,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * event, see if that event was in the delete icon. If so, delete it. * Otherwise, unselect the chip. */ - public void onClick(RecipientChip chip, int offset, float x, float y) { + public void onClick(DrawableRecipientChip chip, int offset, float x, float y) { if (chip.isSelected()) { if (isInDelete(chip, offset, x, y)) { removeChip(chip); @@ -2186,9 +2194,9 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements if (TextUtils.isEmpty(s)) { // Remove all the chips spans. Spannable spannable = getSpannable(); - RecipientChip[] chips = spannable.getSpans(0, getText().length(), - RecipientChip.class); - for (RecipientChip chip : chips) { + DrawableRecipientChip[] chips = spannable.getSpans(0, getText().length(), + DrawableRecipientChip.class); + for (DrawableRecipientChip chip : chips) { spannable.removeSpan(chip); } if (mMoreChip != null) { @@ -2202,16 +2210,23 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements return; } // If the user is editing a chip, don't clear it. - if (mSelectedChip != null - && shouldShowEditableText(mSelectedChip)) { - setCursorVisible(true); - setSelection(getText().length()); - clearSelectedChip(); + if (mSelectedChip != null) { + if (!isGeneratedContact(mSelectedChip)) { + setCursorVisible(true); + setSelection(getText().length()); + clearSelectedChip(); + } else { + return; + } } int length = s.length(); // Make sure there is content there to parse and that it is // not just the commit character. if (length > 1) { + if (lastCharacterIsCommitCharacter(s)) { + commitByCharacter(); + return; + } char last; int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1; int len = length() - 1; @@ -2220,9 +2235,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } else { last = s.charAt(len); } - if (last == COMMIT_CHAR_SEMICOLON || last == COMMIT_CHAR_COMMA) { - commitByCharacter(); - } else if (last == COMMIT_CHAR_SPACE) { + if (last == COMMIT_CHAR_SPACE) { if (!isPhoneQuery()) { // Check if this is a valid email address. If it is, // commit it. @@ -2241,14 +2254,15 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - // This is a delete; check to see if the insertion point is on a space + // The user deleted some text OR some text was replaced; check to + // see if the insertion point is on a space // following a chip. if (before - count == 1) { // If the item deleted is a space, and the thing before the // space is a chip, delete the entire span. int selStart = getSelectionStart(); - RecipientChip[] repl = getSpannable().getSpans(selStart, selStart, - RecipientChip.class); + DrawableRecipientChip[] repl = getSpannable().getSpans(selStart, selStart, + DrawableRecipientChip.class); if (repl.length > 0) { // There is a chip there! Just remove it. Editable editable = getText(); @@ -2262,6 +2276,14 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements editable.delete(tokenStart, tokenEnd); getSpannable().removeSpan(repl[0]); } + } else if (count > before) { + if (mSelectedChip != null + && isGeneratedContact(mSelectedChip)) { + if (lastCharacterIsCommitCharacter(s)) { + commitByCharacter(); + return; + } + } } } @@ -2271,6 +2293,24 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } } + public boolean lastCharacterIsCommitCharacter(CharSequence s) { + char last; + int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1; + int len = length() - 1; + if (end != len) { + last = s.charAt(end); + } else { + last = s.charAt(len); + } + return last == COMMIT_CHAR_COMMA || last == COMMIT_CHAR_SEMICOLON; + } + + public boolean isGeneratedContact(DrawableRecipientChip chip) { + long contactId = chip.getContactId(); + return contactId == RecipientEntry.INVALID_CONTACT + || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT); + } + /** * Handles pasting a {@link ClipData} to this {@link RecipientEditTextView}. */ @@ -2309,7 +2349,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } private void handlePasteAndReplace() { - ArrayList<RecipientChip> created = handlePaste(); + ArrayList<DrawableRecipientChip> created = handlePaste(); if (created != null && created.size() > 0) { // Perform reverse lookups on the pasted contacts. IndividualReplacementTask replace = new IndividualReplacementTask(); @@ -2318,27 +2358,30 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } // Visible for testing. - /* package */ArrayList<RecipientChip> handlePaste() { + /* package */ArrayList<DrawableRecipientChip> handlePaste() { String text = getText().toString(); int originalTokenStart = mTokenizer.findTokenStart(text, getSelectionEnd()); String lastAddress = text.substring(originalTokenStart); int tokenStart = originalTokenStart; - int prevTokenStart = tokenStart; - RecipientChip findChip = null; - ArrayList<RecipientChip> created = new ArrayList<RecipientChip>(); + int prevTokenStart = 0; + DrawableRecipientChip findChip = null; + ArrayList<DrawableRecipientChip> created = new ArrayList<DrawableRecipientChip>(); if (tokenStart != 0) { // There are things before this! - while (tokenStart != 0 && findChip == null) { + while (tokenStart != 0 && findChip == null && tokenStart != prevTokenStart) { prevTokenStart = tokenStart; tokenStart = mTokenizer.findTokenStart(text, tokenStart); findChip = findChip(tokenStart); + if (tokenStart == originalTokenStart && findChip == null) { + break; + } } if (tokenStart != originalTokenStart) { if (findChip != null) { tokenStart = prevTokenStart; } int tokenEnd; - RecipientChip createdChip; + DrawableRecipientChip createdChip; while (tokenStart < originalTokenStart) { tokenEnd = movePastTerminators(mTokenizer.findTokenEnd(getText().toString(), tokenStart)); @@ -2382,12 +2425,12 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } private class RecipientReplacementTask extends AsyncTask<Void, Void, Void> { - private RecipientChip createFreeChip(RecipientEntry entry) { + private DrawableRecipientChip createFreeChip(RecipientEntry entry) { try { if (mNoChips) { return null; } - return constructChipSpan(entry, -1, false, + return constructChipSpan(entry, false, false /*leave space for contact icon */); } catch (NullPointerException e) { Log.e(TAG, e.getMessage(), e); @@ -2396,6 +2439,35 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements } @Override + protected void onPreExecute() { + // Ensure everything is in chip-form already, so we don't have text that slowly gets + // replaced + final List<DrawableRecipientChip> originalRecipients = + new ArrayList<DrawableRecipientChip>(); + final DrawableRecipientChip[] existingChips = getSortedRecipients(); + for (int i = 0; i < existingChips.length; i++) { + originalRecipients.add(existingChips[i]); + } + if (mRemovedSpans != null) { + originalRecipients.addAll(mRemovedSpans); + } + + final List<DrawableRecipientChip> replacements = + new ArrayList<DrawableRecipientChip>(originalRecipients.size()); + + for (final DrawableRecipientChip chip : originalRecipients) { + if (RecipientEntry.isCreatedRecipient(chip.getEntry().getContactId()) + && getSpannable().getSpanStart(chip) != -1) { + replacements.add(createFreeChip(chip.getEntry())); + } else { + replacements.add(null); + } + } + + processReplacements(originalRecipients, replacements); + } + + @Override protected Void doInBackground(Void... params) { if (mIndividualReplacements != null) { mIndividualReplacements.cancel(true); @@ -2403,115 +2475,192 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements // For each chip in the list, look up the matching contact. // If there is a match, replace that chip with the matching // chip. - final ArrayList<RecipientChip> originalRecipients = new ArrayList<RecipientChip>(); - RecipientChip[] existingChips = getSortedRecipients(); + final ArrayList<DrawableRecipientChip> recipients = + new ArrayList<DrawableRecipientChip>(); + DrawableRecipientChip[] existingChips = getSortedRecipients(); for (int i = 0; i < existingChips.length; i++) { - originalRecipients.add(existingChips[i]); + recipients.add(existingChips[i]); } if (mRemovedSpans != null) { - originalRecipients.addAll(mRemovedSpans); + recipients.addAll(mRemovedSpans); } ArrayList<String> addresses = new ArrayList<String>(); - RecipientChip chip; - for (int i = 0; i < originalRecipients.size(); i++) { - chip = originalRecipients.get(i); + DrawableRecipientChip chip; + for (int i = 0; i < recipients.size(); i++) { + chip = recipients.get(i); if (chip != null) { addresses.add(createAddressText(chip.getEntry())); } } - HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter - .getMatchingRecipients(getContext(), addresses); - final ArrayList<RecipientChip> replacements = new ArrayList<RecipientChip>(); - for (final RecipientChip temp : originalRecipients) { - RecipientEntry entry = null; - if (RecipientEntry.isCreatedRecipient(temp.getEntry().getContactId()) - && getSpannable().getSpanStart(temp) != -1) { - // Replace this. - entry = createValidatedEntry(entries.get(tokenizeAddress(temp.getEntry() - .getDestination()))); - } - if (entry != null) { - replacements.add(createFreeChip(entry)); - } else { - replacements.add(temp); - } + final BaseRecipientAdapter adapter = (BaseRecipientAdapter) getAdapter(); + if (adapter == null) { + return null; } + RecipientAlternatesAdapter.getMatchingRecipients(getContext(), addresses, + adapter.getAccount(), new RecipientMatchCallback() { + @Override + public void matchesFound(Map<String, RecipientEntry> entries) { + final ArrayList<DrawableRecipientChip> replacements = + new ArrayList<DrawableRecipientChip>(); + for (final DrawableRecipientChip temp : recipients) { + RecipientEntry entry = null; + if (temp != null && RecipientEntry.isCreatedRecipient( + temp.getEntry().getContactId()) + && getSpannable().getSpanStart(temp) != -1) { + // Replace this. + entry = createValidatedEntry( + entries.get(tokenizeAddress(temp.getEntry() + .getDestination()))); + } + if (entry != null) { + replacements.add(createFreeChip(entry)); + } else { + replacements.add(null); + } + } + processReplacements(recipients, replacements); + } + + @Override + public void matchesNotFound(final Set<String> unfoundAddresses) { + final List<DrawableRecipientChip> replacements = + new ArrayList<DrawableRecipientChip>(unfoundAddresses.size()); + + for (final DrawableRecipientChip temp : recipients) { + if (temp != null && RecipientEntry.isCreatedRecipient( + temp.getEntry().getContactId()) + && getSpannable().getSpanStart(temp) != -1) { + if (unfoundAddresses.contains( + temp.getEntry().getDestination())) { + replacements.add(createFreeChip(temp.getEntry())); + } else { + replacements.add(null); + } + } else { + replacements.add(null); + } + } + + processReplacements(recipients, replacements); + } + }); + return null; + } + + private void processReplacements(final List<DrawableRecipientChip> recipients, + final List<DrawableRecipientChip> replacements) { if (replacements != null && replacements.size() > 0) { - mHandler.post(new Runnable() { + final Runnable runnable = new Runnable() { @Override public void run() { - Editable oldText = getText(); - int start, end; + final Editable text = new SpannableStringBuilder(getText()); int i = 0; - for (RecipientChip chip : originalRecipients) { - // Find the location of the chip in the text currently shown. - start = oldText.getSpanStart(chip); - if (start != -1) { - end = oldText.getSpanEnd(chip); - oldText.removeSpan(chip); - RecipientChip replacement = replacements.get(i); - // Make sure we always have just 1 space at the - // end to separate this chip from the next chip. - SpannableString displayText = new SpannableString( - createAddressText(replacement.getEntry()).trim() + " "); - displayText.setSpan(replacement, 0, displayText.length()-1, - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - // Replace the old text we found with with the new display text, - // which now may also contain the display name of the recipient. - oldText.replace(start, end, displayText); - replacement.setOriginalText(displayText.toString()); + for (final DrawableRecipientChip chip : recipients) { + final DrawableRecipientChip replacement = replacements.get(i); + if (replacement != null) { + final RecipientEntry oldEntry = chip.getEntry(); + final RecipientEntry newEntry = replacement.getEntry(); + final boolean isBetter = + RecipientAlternatesAdapter.getBetterRecipient( + oldEntry, newEntry) == newEntry; + + if (isBetter) { + // Find the location of the chip in the text currently shown. + final int start = text.getSpanStart(chip); + if (start != -1) { + // Replacing the entirety of what the chip represented, + // including the extra space dividing it from other chips. + final int end = + Math.min(text.getSpanEnd(chip) + 1, text.length()); + text.removeSpan(chip); + // Make sure we always have just 1 space at the end to + // separate this chip from the next chip. + final SpannableString displayText = + new SpannableString(createAddressText( + replacement.getEntry()).trim() + " "); + displayText.setSpan(replacement, 0, + displayText.length() - 1, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + // Replace the old text we found with with the new display + // text, which now may also contain the display name of the + // recipient. + text.replace(start, end, displayText); + replacement.setOriginalText(displayText.toString()); + replacements.set(i, null); + + recipients.set(i, replacement); + } + } } i++; } - originalRecipients.clear(); + setText(text); } - }); + }; + + if (Looper.myLooper() == Looper.getMainLooper()) { + runnable.run(); + } else { + mHandler.post(runnable); + } } - return null; } } - private class IndividualReplacementTask extends AsyncTask<Object, Void, Void> { - @SuppressWarnings("unchecked") + private class IndividualReplacementTask + extends AsyncTask<ArrayList<DrawableRecipientChip>, Void, Void> { @Override - protected Void doInBackground(Object... params) { + protected Void doInBackground(ArrayList<DrawableRecipientChip>... params) { // For each chip in the list, look up the matching contact. // If there is a match, replace that chip with the matching // chip. - final ArrayList<RecipientChip> originalRecipients = - (ArrayList<RecipientChip>) params[0]; + final ArrayList<DrawableRecipientChip> originalRecipients = params[0]; ArrayList<String> addresses = new ArrayList<String>(); - RecipientChip chip; + DrawableRecipientChip chip; for (int i = 0; i < originalRecipients.size(); i++) { chip = originalRecipients.get(i); if (chip != null) { addresses.add(createAddressText(chip.getEntry())); } } - HashMap<String, RecipientEntry> entries = RecipientAlternatesAdapter - .getMatchingRecipients(getContext(), addresses); - for (final RecipientChip temp : originalRecipients) { - if (RecipientEntry.isCreatedRecipient(temp.getEntry().getContactId()) - && getSpannable().getSpanStart(temp) != -1) { - // Replace this. - RecipientEntry entry = createValidatedEntry(entries.get(tokenizeAddress( - temp.getEntry().getDestination()).toLowerCase())); - // If we don't have a validated contact match, just use the - // entry as it existed before. - if (entry == null && !isPhoneQuery()) { - entry = temp.getEntry(); - } - final RecipientEntry tempEntry = entry; - if (tempEntry != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - replaceChip(temp, tempEntry); + RecipientAlternatesAdapter.getMatchingRecipients(getContext(), addresses, + ((BaseRecipientAdapter) getAdapter()).getAccount(), + new RecipientMatchCallback() { + + @Override + public void matchesFound(Map<String, RecipientEntry> entries) { + for (final DrawableRecipientChip temp : originalRecipients) { + if (RecipientEntry.isCreatedRecipient(temp.getEntry() + .getContactId()) + && getSpannable().getSpanStart(temp) != -1) { + // Replace this. + RecipientEntry entry = createValidatedEntry(entries + .get(tokenizeAddress(temp.getEntry().getDestination()) + .toLowerCase())); + // If we don't have a validated contact + // match, just use the + // entry as it existed before. + if (entry == null && !isPhoneQuery()) { + entry = temp.getEntry(); + } + final RecipientEntry tempEntry = entry; + if (tempEntry != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + replaceChip(temp, tempEntry); + } + }); + } + } } - }); - } - } - } + } + + @Override + public void matchesNotFound(final Set<String> unfoundAddresses) { + // No action required + } + }); return null; } } @@ -2546,7 +2695,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements float x = event.getX(); float y = event.getY(); int offset = putOffsetInRange(getOffsetForPosition(x, y)); - RecipientChip currentChip = findChip(offset); + DrawableRecipientChip currentChip = findChip(offset); if (currentChip != null) { if (mDragEnabled) { // Start drag-and-drop for the selected chip. @@ -2568,7 +2717,7 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements /** * Starts drag-and-drop for the selected chip. */ - private void startDrag(RecipientChip currentChip) { + private void startDrag(DrawableRecipientChip currentChip) { String address = currentChip.getEntry().getDestination(); ClipData data = ClipData.newPlainText(address, address + COMMIT_CHAR_COMMA); @@ -2603,22 +2752,22 @@ public class RecipientEditTextView extends MultiAutoCompleteTextView implements * Drag shadow for a {@link RecipientChip}. */ private final class RecipientChipShadow extends DragShadowBuilder { - private final RecipientChip mChip; + private final DrawableRecipientChip mChip; - public RecipientChipShadow(RecipientChip chip) { + public RecipientChipShadow(DrawableRecipientChip chip) { mChip = chip; } @Override public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { - Rect rect = mChip.getDrawable().getBounds(); + Rect rect = mChip.getBounds(); shadowSize.set(rect.width(), rect.height()); shadowTouchPoint.set(rect.centerX(), rect.centerY()); } @Override public void onDrawShadow(Canvas canvas) { - mChip.getDrawable().draw(canvas); + mChip.draw(canvas); } } diff --git a/chips/src/com/android/ex/chips/RecipientEntry.java b/chips/src/com/android/ex/chips/RecipientEntry.java index 0448229..44bc767 100644 --- a/chips/src/com/android/ex/chips/RecipientEntry.java +++ b/chips/src/com/android/ex/chips/RecipientEntry.java @@ -19,6 +19,8 @@ package com.android.ex.chips; import android.net.Uri; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.DisplayNameSources; +import android.text.util.Rfc822Token; +import android.text.util.Rfc822Tokenizer; /** * Represents one entry inside recipient auto-complete list. @@ -65,29 +67,16 @@ public class RecipientEntry { private final Uri mPhotoThumbnailUri; + private boolean mIsValid; /** * This can be updated after this object being constructed, when the photo is fetched * from remote directories. */ private byte[] mPhotoBytes; - private RecipientEntry(int entryType) { - mEntryType = entryType; - mDisplayName = null; - mDestination = null; - mDestinationType = INVALID_DESTINATION_TYPE; - mDestinationLabel = null; - mContactId = -1; - mDataId = -1; - mPhotoThumbnailUri = null; - mPhotoBytes = null; - mIsDivider = true; - } - - private RecipientEntry( - int entryType, String displayName, - String destination, int destinationType, String destinationLabel, - long contactId, long dataId, Uri photoThumbnailUri, boolean isFirstLevel) { + private RecipientEntry(int entryType, String displayName, String destination, + int destinationType, String destinationLabel, long contactId, long dataId, + Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid) { mEntryType = entryType; mIsFirstLevel = isFirstLevel; mDisplayName = displayName; @@ -99,6 +88,11 @@ public class RecipientEntry { mPhotoThumbnailUri = photoThumbnailUri; mPhotoBytes = null; mIsDivider = false; + mIsValid = isValid; + } + + public boolean isValid() { + return mIsValid; } /** @@ -114,10 +108,23 @@ public class RecipientEntry { * This address has not been resolved to a contact and therefore does not * have a contact id or photo. */ - public static RecipientEntry constructFakeEntry(String address) { - return new RecipientEntry(ENTRY_TYPE_PERSON, address, address, + public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) { + final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address); + final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address; + + return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress, + INVALID_DESTINATION_TYPE, null, + INVALID_CONTACT, INVALID_CONTACT, null, true, isValid); + } + + /** + * Construct a RecipientEntry from just a phone number. + */ + public static RecipientEntry constructFakePhoneEntry(final String phoneNumber, + final boolean isValid) { + return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber, INVALID_DESTINATION_TYPE, null, - INVALID_CONTACT, INVALID_CONTACT, null, true); + INVALID_CONTACT, INVALID_CONTACT, null, true, isValid); } /** @@ -136,41 +143,37 @@ public class RecipientEntry { * with both an associated display name. This address has not been resolved * to a contact and therefore does not have a contact id or photo. */ - public static RecipientEntry constructGeneratedEntry(String display, String address) { - return new RecipientEntry(ENTRY_TYPE_PERSON, display, - address, INVALID_DESTINATION_TYPE, null, - GENERATED_CONTACT, GENERATED_CONTACT, null, true); - } - - public static RecipientEntry constructTopLevelEntry( - String displayName, int displayNameSource, String destination, int destinationType, - String destinationLabel, long contactId, long dataId, Uri photoThumbnailUri) { - return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, - destination), - destination, destinationType, destinationLabel, - contactId, dataId, - photoThumbnailUri, true); - } - - public static RecipientEntry constructTopLevelEntry( - String displayName, int displayNameSource, String destination, int destinationType, - String destinationLabel, long contactId, long dataId, - String thumbnailUriAsString) { - return new RecipientEntry( - ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination), - destination, destinationType, destinationLabel, - contactId, dataId, - (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) : null), true); - } - - public static RecipientEntry constructSecondLevelEntry( - String displayName, int displayNameSource, String destination, int destinationType, - String destinationLabel, long contactId, long dataId, String thumbnailUriAsString) { - return new RecipientEntry( - ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, displayName, destination), - destination, destinationType, destinationLabel, - contactId, dataId, - (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) : null), false); + public static RecipientEntry constructGeneratedEntry(String display, String address, + boolean isValid) { + return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE, + null, GENERATED_CONTACT, GENERATED_CONTACT, null, true, isValid); + } + + public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, + String destination, int destinationType, String destinationLabel, long contactId, + long dataId, Uri photoThumbnailUri, boolean isValid) { + return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, + displayName, destination), destination, destinationType, destinationLabel, + contactId, dataId, photoThumbnailUri, true, isValid); + } + + public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource, + String destination, int destinationType, String destinationLabel, long contactId, + long dataId, String thumbnailUriAsString, boolean isValid) { + return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, + displayName, destination), destination, destinationType, destinationLabel, + contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) + : null), true, isValid); + } + + public static RecipientEntry constructSecondLevelEntry(String displayName, + int displayNameSource, String destination, int destinationType, + String destinationLabel, long contactId, long dataId, String thumbnailUriAsString, + boolean isValid) { + return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource, + displayName, destination), destination, destinationType, destinationLabel, + contactId, dataId, (thumbnailUriAsString != null ? Uri.parse(thumbnailUriAsString) + : null), false, isValid); } public int getEntryType() { @@ -226,4 +229,9 @@ public class RecipientEntry { public boolean isSelectable() { return mEntryType == ENTRY_TYPE_PERSON; } + + @Override + public String toString() { + return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid; + } }
\ No newline at end of file diff --git a/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java b/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java index 8131fc3..0571a4e 100644 --- a/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java +++ b/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java @@ -43,7 +43,7 @@ class SingleRecipientArrayAdapter extends ArrayAdapter<RecipientEntry> { if (convertView == null) { convertView = newView(); } - bindView(convertView, convertView.getContext(), getItem(position)); + bindView(convertView, getItem(position)); return convertView; } @@ -51,7 +51,7 @@ class SingleRecipientArrayAdapter extends ArrayAdapter<RecipientEntry> { return mLayoutInflater.inflate(mLayoutId, null); } - private void bindView(View view, Context context, RecipientEntry entry) { + private static void bindView(View view, RecipientEntry entry) { TextView display = (TextView) view.findViewById(android.R.id.title); ImageView imageView = (ImageView) view.findViewById(android.R.id.icon); display.setText(entry.getDisplayName()); diff --git a/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java new file mode 100644 index 0000000..a080ee7 --- /dev/null +++ b/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.chips.recipientchip; + +import com.android.ex.chips.RecipientEntry; + +/** + * BaseRecipientChip defines an object that contains information relevant to a + * particular recipient. + */ +interface BaseRecipientChip { + + /** + * Set the selected state of the chip. + */ + void setSelected(boolean selected); + + /** + * Return true if the chip is selected. + */ + boolean isSelected(); + + /** + * Get the text displayed in the chip. + */ + CharSequence getDisplay(); + + /** + * Get the text value this chip represents. + */ + CharSequence getValue(); + + /** + * Get the id of the contact associated with this chip. + */ + long getContactId(); + + /** + * Get the id of the data associated with this chip. + */ + long getDataId(); + + /** + * Get associated RecipientEntry. + */ + RecipientEntry getEntry(); + + /** + * Set the text in the edittextview originally associated with this chip + * before any reverse lookups. + */ + void setOriginalText(String text); + + /** + * Set the text in the edittextview originally associated with this chip + * before any reverse lookups. + */ + CharSequence getOriginalText(); +} diff --git a/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java new file mode 100644 index 0000000..396a8ac --- /dev/null +++ b/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.chips.recipientchip; + +import android.graphics.Canvas; +import android.graphics.Rect; + +/** + * RecipientChip defines a drawable object that contains information relevant to a + * particular recipient. + */ +public interface DrawableRecipientChip extends BaseRecipientChip { + /** + * Get the bounds of the chip; may be 0,0 if it is not visibly rendered. + */ + Rect getBounds(); + + /** + * Draw the chip. + */ + void draw(Canvas canvas); +} diff --git a/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java new file mode 100644 index 0000000..0380a81 --- /dev/null +++ b/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.chips.recipientchip; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.style.ReplacementSpan; + +import com.android.ex.chips.RecipientEntry; + +/** + * RecipientChip defines a span that contains information relevant to a + * particular recipient. + */ +public class InvisibleRecipientChip extends ReplacementSpan implements DrawableRecipientChip { + private final SimpleRecipientChip mDelegate; + + public InvisibleRecipientChip(final RecipientEntry entry) { + super(); + + mDelegate = new SimpleRecipientChip(entry); + } + + @Override + public void setSelected(final boolean selected) { + mDelegate.setSelected(selected); + } + + @Override + public boolean isSelected() { + return mDelegate.isSelected(); + } + + @Override + public CharSequence getDisplay() { + return mDelegate.getDisplay(); + } + + @Override + public CharSequence getValue() { + return mDelegate.getValue(); + } + + @Override + public long getContactId() { + return mDelegate.getContactId(); + } + + @Override + public long getDataId() { + return mDelegate.getDataId(); + } + + @Override + public RecipientEntry getEntry() { + return mDelegate.getEntry(); + } + + @Override + public void setOriginalText(final String text) { + mDelegate.setOriginalText(text); + } + + @Override + public CharSequence getOriginalText() { + return mDelegate.getOriginalText(); + } + + @Override + public void draw(final Canvas canvas, final CharSequence text, final int start, final int end, + final float x, final int top, final int y, final int bottom, final Paint paint) { + // Do nothing. + } + + @Override + public int getSize(final Paint paint, final CharSequence text, final int start, final int end, + final Paint.FontMetricsInt fm) { + return 0; + } + + @Override + public Rect getBounds() { + return new Rect(0, 0, 0, 0); + } + + @Override + public void draw(final Canvas canvas) { + // do nothing. + } +} diff --git a/chips/src/com/android/ex/chips/RecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java index 0f93a0d..c04b3be 100644 --- a/chips/src/com/android/ex/chips/RecipientChip.java +++ b/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,13 @@ * limitations under the License. */ -package com.android.ex.chips; +package com.android.ex.chips.recipientchip; + +import com.android.ex.chips.RecipientEntry; -import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.text.style.DynamicDrawableSpan; -import android.text.style.ImageSpan; -/** - * RecipientChip defines an ImageSpan that contains information relevant to a - * particular recipient. - */ -/* package */class RecipientChip extends ImageSpan { +class SimpleRecipientChip implements BaseRecipientChip { private final CharSequence mDisplay; private final CharSequence mValue; @@ -34,14 +29,13 @@ import android.text.style.ImageSpan; private final long mDataId; - private RecipientEntry mEntry; + private final RecipientEntry mEntry; private boolean mSelected = false; private CharSequence mOriginalText; - public RecipientChip(Drawable drawable, RecipientEntry entry, int offset) { - super(drawable, DynamicDrawableSpan.ALIGN_BOTTOM); + public SimpleRecipientChip(final RecipientEntry entry) { mDisplay = entry.getDisplayName(); mValue = entry.getDestination().trim(); mContactId = entry.getContactId(); @@ -49,64 +43,57 @@ import android.text.style.ImageSpan; mEntry = entry; } - /** - * Set the selected state of the chip. - * @param selected - */ - public void setSelected(boolean selected) { + @Override + public void setSelected(final boolean selected) { mSelected = selected; } - /** - * Return true if the chip is selected. - */ + @Override public boolean isSelected() { return mSelected; } - /** - * Get the text displayed in the chip. - */ + @Override public CharSequence getDisplay() { return mDisplay; } - /** - * Get the text value this chip represents. - */ + @Override public CharSequence getValue() { return mValue; } - /** - * Get the id of the contact associated with this chip. - */ + @Override public long getContactId() { return mContactId; } - /** - * Get the id of the data associated with this chip. - */ + @Override public long getDataId() { return mDataId; } - /** - * Get associated RecipientEntry. - */ + @Override public RecipientEntry getEntry() { return mEntry; } - public void setOriginalText(String text) { - if (!TextUtils.isEmpty(text)) { - text = text.trim(); + @Override + public void setOriginalText(final String text) { + if (TextUtils.isEmpty(text)) { + mOriginalText = text; + } else { + mOriginalText = text.trim(); } - mOriginalText = text; } + @Override public CharSequence getOriginalText() { return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination(); } -} + + @Override + public String toString() { + return mDisplay + " <" + mValue + ">"; + } +}
\ No newline at end of file diff --git a/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java new file mode 100644 index 0000000..acade7f --- /dev/null +++ b/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ex.chips.recipientchip; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; +import android.text.style.ImageSpan; + +import com.android.ex.chips.RecipientEntry; + +/** + * VisibleRecipientChip defines an ImageSpan that contains information relevant to a + * particular recipient and renders a background asset to go with it. + */ +public class VisibleRecipientChip extends ImageSpan implements DrawableRecipientChip { + private final SimpleRecipientChip mDelegate; + + public VisibleRecipientChip(final Drawable drawable, final RecipientEntry entry) { + super(drawable, DynamicDrawableSpan.ALIGN_BOTTOM); + + mDelegate = new SimpleRecipientChip(entry); + } + + @Override + public void setSelected(final boolean selected) { + mDelegate.setSelected(selected); + } + + @Override + public boolean isSelected() { + return mDelegate.isSelected(); + } + + @Override + public CharSequence getDisplay() { + return mDelegate.getDisplay(); + } + + @Override + public CharSequence getValue() { + return mDelegate.getValue(); + } + + @Override + public long getContactId() { + return mDelegate.getContactId(); + } + + @Override + public long getDataId() { + return mDelegate.getDataId(); + } + + @Override + public RecipientEntry getEntry() { + return mDelegate.getEntry(); + } + + @Override + public void setOriginalText(final String text) { + mDelegate.setOriginalText(text); + } + + @Override + public CharSequence getOriginalText() { + return mDelegate.getOriginalText(); + } + + @Override + public Rect getBounds() { + return getDrawable().getBounds(); + } + + @Override + public void draw(final Canvas canvas) { + getDrawable().draw(canvas); + } + + @Override + public String toString() { + return mDelegate.toString(); + } +} diff --git a/chips/tests/Android.mk b/chips/tests/Android.mk index 313af7d..b01aa0c 100644 --- a/chips/tests/Android.mk +++ b/chips/tests/Android.mk @@ -20,7 +20,6 @@ LOCAL_PACKAGE_NAME := ChipsTests LOCAL_MODULE_TAGS := tests LOCAL_SDK_VERSION := current LOCAL_CERTIFICATE := platform -LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_STATIC_JAVA_LIBRARIES += android-common-chips LOCAL_RESOURCE_DIR := frameworks/ex/chips/res/ LOCAL_AAPT_FLAGS := --auto-add-overlay diff --git a/chips/tests/src/com/android/ex/chips/ChipsTest.java b/chips/tests/src/com/android/ex/chips/ChipsTest.java index 6639bcb..7963086 100644 --- a/chips/tests/src/com/android/ex/chips/ChipsTest.java +++ b/chips/tests/src/com/android/ex/chips/ChipsTest.java @@ -28,12 +28,17 @@ import android.text.style.ImageSpan; import android.text.util.Rfc822Tokenizer; import android.widget.TextView; +import com.android.ex.chips.BaseRecipientAdapter; import com.android.ex.chips.RecipientEditTextView; import com.android.ex.chips.RecipientEntry; +import com.android.ex.chips.recipientchip.DrawableRecipientChip; +import com.android.ex.chips.recipientchip.VisibleRecipientChip;; + +import java.util.regex.Pattern; @SmallTest public class ChipsTest extends AndroidTestCase { - private RecipientChip[] mMockRecips; + private DrawableRecipientChip[] mMockRecips; private RecipientEntry[] mMockEntries; @@ -50,7 +55,7 @@ public class ChipsTest extends AndroidTestCase { } @Override - public RecipientChip[] getSortedRecipients() { + public DrawableRecipientChip[] getSortedRecipients() { return mMockRecips; } @@ -79,7 +84,7 @@ public class ChipsTest extends AndroidTestCase { } @Override - public RecipientChip[] getSortedRecipients() { + public DrawableRecipientChip[] getSortedRecipients() { return mMockRecips; } @@ -120,9 +125,14 @@ public class ChipsTest extends AndroidTestCase { } private class TestBaseRecipientAdapter extends BaseRecipientAdapter { - public TestBaseRecipientAdapter(Context context) { + public TestBaseRecipientAdapter(final Context context) { super(context); } + + public TestBaseRecipientAdapter(final Context context, final int preferredMaxResultCount, + final int queryMode) { + super(context, preferredMaxResultCount, queryMode); + } } private MockRecipientEditTextView createViewForTesting() { @@ -135,15 +145,15 @@ public class ChipsTest extends AndroidTestCase { public void testCreateDisplayText() { RecipientEditTextView view = createViewForTesting(); RecipientEntry entry = RecipientEntry.constructGeneratedEntry("User Name, Jr", - "user@username.com"); + "user@username.com", true); String testAddress = view.createAddressText(entry); String testDisplay = view.createChipDisplayText(entry); assertEquals("Expected a properly formatted RFC email address", "\"User Name, Jr\" <user@username.com>, ", testAddress); assertEquals("Expected a displayable name", "User Name, Jr", testDisplay); - - RecipientEntry alreadyFormatted = RecipientEntry.constructFakeEntry("user@username.com, "); + RecipientEntry alreadyFormatted = + RecipientEntry.constructFakeEntry("user@username.com, ", true); testAddress = view.createAddressText(alreadyFormatted); testDisplay = view.createChipDisplayText(alreadyFormatted); assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ", @@ -151,13 +161,13 @@ public class ChipsTest extends AndroidTestCase { assertEquals("Expected a displayable name", "user@username.com", testDisplay); RecipientEntry alreadyFormattedNoSpace = RecipientEntry - .constructFakeEntry("user@username.com,"); + .constructFakeEntry("user@username.com,", true); testAddress = view.createAddressText(alreadyFormattedNoSpace); assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ", testAddress); RecipientEntry alreadyNamed = RecipientEntry.constructGeneratedEntry("User Name", - "\"User Name, Jr\" <user@username.com>"); + "\"User Name, Jr\" <user@username.com>", true); testAddress = view.createAddressText(alreadyNamed); testDisplay = view.createChipDisplayText(alreadyNamed); assertEquals( @@ -254,8 +264,8 @@ public class ChipsTest extends AndroidTestCase { String first = (String) mTokenizer.terminateToken("FIRST"); String second = (String) mTokenizer.terminateToken("SECOND"); String third = (String) mTokenizer.terminateToken("THIRD"); - String fourth = (String) ("FOURTH,"); - String fifth = (String) ("FIFTH,"); + String fourth = "FOURTH,"; + String fifth = "FIFTH,"; mEditable = new SpannableStringBuilder(); mEditable.append(first+second+third+fourth+fifth); assertEquals(view.countTokens(mEditable), 5); @@ -620,7 +630,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); assertEquals(mEditable.toString(), first + second + third); view.replaceChip(mMockRecips[mMockRecips.length - 3], RecipientEntry - .constructGeneratedEntry("replacement", "replacement@replacement.com")); + .constructGeneratedEntry("replacement", "replacement@replacement.com", true)); assertEquals(mEditable.toString(), mTokenizer .terminateToken("replacement <replacement@replacement.com>") + second + third); @@ -636,10 +646,11 @@ public class ChipsTest extends AndroidTestCase { assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable .toString().indexOf(third) + third.trim().length()); - RecipientChip[] spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class); + DrawableRecipientChip[] spans = + mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); assertEquals(spans.length, 3); spans = mEditable - .getSpans(0, mEditable.toString().indexOf(second) - 1, RecipientChip.class); + .getSpans(0, mEditable.toString().indexOf(second) - 1, DrawableRecipientChip.class); assertEquals((String) spans[0].getDisplay(), "replacement"); @@ -657,7 +668,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); assertEquals(mEditable.toString(), first + second + third); view.replaceChip(mMockRecips[mMockRecips.length - 2], RecipientEntry - .constructGeneratedEntry("replacement", "replacement@replacement.com")); + .constructGeneratedEntry("replacement", "replacement@replacement.com", true)); assertEquals(mEditable.toString(), first + mTokenizer .terminateToken("replacement <replacement@replacement.com>") + third); assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); @@ -669,10 +680,10 @@ public class ChipsTest extends AndroidTestCase { assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable .toString().indexOf(third) + third.trim().length()); - spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class); + spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); assertEquals(spans.length, 3); - spans = mEditable - .getSpans(firstEnd, mEditable.toString().indexOf(third) - 1, RecipientChip.class); + spans = mEditable.getSpans(firstEnd, mEditable.toString().indexOf(third) - 1, + DrawableRecipientChip.class); assertEquals((String) spans[0].getDisplay(), "replacement"); @@ -690,7 +701,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0); assertEquals(mEditable.toString(), first + second + third); view.replaceChip(mMockRecips[mMockRecips.length - 1], RecipientEntry - .constructGeneratedEntry("replacement", "replacement@replacement.com")); + .constructGeneratedEntry("replacement", "replacement@replacement.com", true)); assertEquals(mEditable.toString(), first + second + mTokenizer .terminateToken("replacement <replacement@replacement.com>")); assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart); @@ -699,10 +710,10 @@ public class ChipsTest extends AndroidTestCase { assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd); assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1); assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1); - spans = mEditable.getSpans(0, mEditable.length(), RecipientChip.class); + spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class); assertEquals(spans.length, 3); spans = mEditable - .getSpans(secondEnd, mEditable.length(), RecipientChip.class); + .getSpans(secondEnd, mEditable.length(), DrawableRecipientChip.class); assertEquals((String) spans[0].getDisplay(), "replacement"); } @@ -717,7 +728,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user@user.com"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, 0); + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0); assertEquals(mEditable.toString(), "user@user.com"); // Test adding a single address to an empty chips field with a space at @@ -727,7 +738,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.append(tokenizedUser); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, 0); + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0); assertEquals(mEditable.toString(), tokenizedUser); // Test adding a single address to an empty chips field with a semicolon at @@ -737,7 +748,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.append(tokenizedUser); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, 1); + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1); // Test adding 2 address to an empty chips field. The second to last // address should become a chip and the last address should stay as @@ -746,9 +757,9 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user1,user2@user.com"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, 1); + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1); assertEquals(mEditable.getSpans(0, mEditable.toString().indexOf("user2@user.com"), - RecipientChip.class).length, 1); + DrawableRecipientChip.class).length, 1); assertEquals(mEditable.toString(), "<user1>, user2@user.com"); // Test adding a single address to the end of existing chips. The existing @@ -773,7 +784,7 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user@user.com"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, mMockRecips.length); assertEquals(mEditable.toString(), first + second + third + "user@user.com"); @@ -790,12 +801,12 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user1, user2@user.com"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, mMockRecips.length + 1); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable - .toString().indexOf("user2@user.com") - 1, RecipientChip.class).length, 1); + .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable - .length(), RecipientChip.class).length, 0); + .length(), DrawableRecipientChip.class).length, 0); assertEquals(mEditable.toString(), first + second + third + "<user1>, user2@user.com"); // Paste 2 addresses after existing chips. We expect the first address to be turned into @@ -812,12 +823,12 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user1,user2@user.com"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, mMockRecips.length + 1); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable - .toString().indexOf("user2@user.com") - 1, RecipientChip.class).length, 1); + .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable - .length(), RecipientChip.class).length, 0); + .length(), DrawableRecipientChip.class).length, 0); assertEquals(mEditable.toString(), first + second + third + "<user1>, user2@user.com"); // Test a complete token pasted in at the end. It should be turned into a chip. @@ -825,11 +836,11 @@ public class ChipsTest extends AndroidTestCase { mEditable.append("user1, user2@user.com,"); view.setSelection(mEditable.length()); view.handlePaste(); - assertEquals(mEditable.getSpans(0, mEditable.length(), RecipientChip.class).length, 2); + assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 2); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable - .toString().indexOf("user2@user.com") - 1, RecipientChip.class).length, 1); + .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1); assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable - .length(), RecipientChip.class).length, 1); + .length(), DrawableRecipientChip.class).length, 1); assertEquals(mEditable.toString(), "<user1>, <user2@user.com>, "); } @@ -922,11 +933,96 @@ public class ChipsTest extends AndroidTestCase { mMockEntries = new RecipientEntry[size]; for (int i = 0; i < size; i++) { mMockEntries[i] = RecipientEntry.constructGeneratedEntry("user", - "user@username.com"); + "user@username.com", true); } - mMockRecips = new RecipientChip[size]; + mMockRecips = new DrawableRecipientChip[size]; for (int i = 0; i < size; i++) { - mMockRecips[i] = new RecipientChip(null, mMockEntries[i], i); + mMockRecips[i] = new VisibleRecipientChip(null, mMockEntries[i]); } } + + /** + * <p> + * Ensure the original text is always accurate, regardless of the type of email. The original + * text is used to determine where to display the chip span. If this test fails, it means some + * text that should be turned into one whole chip may behave unexpectedly. + * </p> + * <p> + * For example, a bug was seen where + * + * <pre> + * "Android User" <android@example.com> + * </pre> + * + * was converted to + * + * <pre> + * Android User [android@example.com] + * </pre> + * + * where text inside [] is a chip. + * </p> + */ + public void testCreateReplacementChipOriginalText() { + // Name in quotes + email address + testCreateReplacementChipOriginalText("\"Android User\" <android@example.com>,"); + // Name in quotes + email address without brackets + testCreateReplacementChipOriginalText("\"Android User\" android@example.com,"); + // Name in quotes + testCreateReplacementChipOriginalText("\"Android User\","); + // Name without quotes + email address + testCreateReplacementChipOriginalText("Android User <android@example.com>,"); + // Name without quotes + testCreateReplacementChipOriginalText("Android User,"); + // Email address + testCreateReplacementChipOriginalText("<android@example.com>,"); + // Email address without brackets + testCreateReplacementChipOriginalText("android@example.com,"); + } + + private void testCreateReplacementChipOriginalText(final String email) { + // No trailing space + attemptCreateReplacementChipOriginalText(email.trim()); + // Trailing space + attemptCreateReplacementChipOriginalText(email.trim() + " "); + } + + private void attemptCreateReplacementChipOriginalText(final String email) { + final RecipientEditTextView view = new RecipientEditTextView(getContext(), null); + + view.setText(email); + view.mPendingChips.add(email); + + view.createReplacementChip(0, email.length(), view.getText(), true); + // The "original text" should be the email without the comma or space(s) + assertEquals(email.replaceAll(",\\s*$", ""), + view.mTemporaryRecipients.get(0).getOriginalText().toString().trim()); + } + + public void testCreateTokenizedEntryForPhone() { + final String phonePattern = "[^\\d]*888[^\\d]*555[^\\d]*1234[^\\d]*"; + final String phone1 = "8885551234"; + final String phone2 = "888-555-1234"; + final String phone3 = "(888) 555-1234"; + + final RecipientEditTextView view = new RecipientEditTextView(getContext(), null); + final BaseRecipientAdapter adapter = new TestBaseRecipientAdapter(getContext(), 10, + BaseRecipientAdapter.QUERY_TYPE_PHONE); + view.setAdapter(adapter); + + final RecipientEntry entry1 = view.createTokenizedEntry(phone1); + final String destination1 = entry1.getDestination(); + assertTrue(phone1 + " failed with " + destination1, + Pattern.matches(phonePattern, destination1)); + + final RecipientEntry entry2 = view.createTokenizedEntry(phone2); + final String destination2 = entry2.getDestination(); + assertTrue(phone2 + " failed with " + destination2, + Pattern.matches(phonePattern, destination2)); + + final RecipientEntry entry3 = view.createTokenizedEntry(phone3); + final String destination3 = entry3.getDestination(); + assertTrue(phone3 + " failed with " + destination3, + Pattern.matches(phonePattern, destination3)); + } } diff --git a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java b/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java index f4b87c0..a1a1c7a 100644 --- a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java +++ b/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java @@ -18,8 +18,13 @@ package com.android.ex.chips; import android.database.Cursor; import android.database.MatrixCursor; +import android.net.Uri; +import android.provider.ContactsContract.DisplayNameSources; import android.test.AndroidTestCase; +import com.android.ex.chips.RecipientAlternatesAdapter; +import com.android.ex.chips.RecipientEntry; + public class RecipientAlternatesAdapterTest extends AndroidTestCase { public void testRemoveDuplicateDestinations() { @@ -99,4 +104,55 @@ public class RecipientAlternatesAdapterTest extends AndroidTestCase { assertEquals(photoUri, c.getString(6)); assertEquals(displayNameSource, c.getInt(7)); } + + public void testGetBetterRecipient() { + // Ensure that if either (but not both) parameters are null, the other is returned + { + final RecipientEntry entry1 = + RecipientEntry.constructFakeEntry("1@android.com", true); + final RecipientEntry entry2 = null; + + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); + } + + // Ensure that if only one has a display name, it is used + { + final RecipientEntry entry1 = + RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, + "1@android.com", 0, null, 0, 0, (Uri) null, true); + final RecipientEntry entry2 = RecipientEntry.constructFakeEntry("1@android.com", true); + + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); + } + + // Ensure that if one has a display name different from its destination, and the other's + // is equal to its destination, we use the unique one + { + final RecipientEntry entry1 = + RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, + "1@android.com", 0, null, 0, 0, (Uri) null, true); + final RecipientEntry entry2 = + RecipientEntry.constructTopLevelEntry("2@android.com", DisplayNameSources.EMAIL, + "2@android.com", 0, null, 0, 0, (Uri) null, true); + + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); + } + + // Ensure that if only one has a photo, it is used + { + final RecipientEntry entry1 = + RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME, + "1@android.com", 0, null, 0, 0, Uri.parse("http://www.android.com"), + true); + final RecipientEntry entry2 = + RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.EMAIL, + "2@android.com", 0, null, 0, 0, (Uri) null, true); + + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1); + assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1); + } + } } diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java index b8fc7bc..261b15d 100644 --- a/common/java/com/android/common/OperationScheduler.java +++ b/common/java/com/android/common/OperationScheduler.java @@ -42,6 +42,9 @@ public class OperationScheduler { /** Wait this long times the number of consecutive errors so far before retrying. */ public long backoffIncrementalMillis = 5000; + /** Wait this long times 2^(number of consecutive errors so far) before retrying. */ + public int backoffExponentialMillis = 0; + /** Maximum duration of moratorium to honor. Mostly an issue for clock rollbacks. */ public long maxMoratoriumMillis = 24 * 3600 * 1000; @@ -53,11 +56,20 @@ public class OperationScheduler { @Override public String toString() { - return String.format( + if (backoffExponentialMillis > 0) { + return String.format( + "OperationScheduler.Options[backoff=%.1f+%.1f+%.1f max=%.1f min=%.1f period=%.1f]", + backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0, + backoffExponentialMillis / 1000.0, + maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0, + periodicIntervalMillis / 1000.0); + } else { + return String.format( "OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]", backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0, maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0, periodicIntervalMillis / 1000.0); + } } } @@ -76,7 +88,7 @@ public class OperationScheduler { * Parse scheduler options supplied in this string form: * * <pre> - * backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval) + * backoff=(fixed)+(incremental)[+(exponential)] max=(maxmoratorium) min=(mintrigger) [period=](interval) * </pre> * * All values are times in (possibly fractional) <em>seconds</em> (not milliseconds). @@ -97,14 +109,18 @@ public class OperationScheduler { for (String param : spec.split(" +")) { if (param.length() == 0) continue; if (param.startsWith("backoff=")) { - int plus = param.indexOf('+', 8); - if (plus < 0) { - options.backoffFixedMillis = parseSeconds(param.substring(8)); - } else { - if (plus > 8) { - options.backoffFixedMillis = parseSeconds(param.substring(8, plus)); - } - options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1)); + String[] pieces = param.substring(8).split("\\+"); + if (pieces.length > 3) { + throw new IllegalArgumentException("bad value for backoff: [" + spec + "]"); + } + if (pieces.length > 0 && pieces[0].length() > 0) { + options.backoffFixedMillis = parseSeconds(pieces[0]); + } + if (pieces.length > 1 && pieces[1].length() > 0) { + options.backoffIncrementalMillis = parseSeconds(pieces[1]); + } + if (pieces.length > 2 && pieces[2].length() > 0) { + options.backoffExponentialMillis = (int)parseSeconds(pieces[2]); } } else if (param.startsWith("max=")) { options.maxMoratoriumMillis = parseSeconds(param.substring(4)); @@ -160,8 +176,21 @@ public class OperationScheduler { time = Math.max(time, moratoriumTimeMillis); time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis); if (errorCount > 0) { - time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis + - options.backoffIncrementalMillis * errorCount); + int shift = errorCount-1; + // backoffExponentialMillis is an int, so we can safely + // double it 30 times without overflowing a long. + if (shift > 30) shift = 30; + long backoff = options.backoffFixedMillis + + (options.backoffIncrementalMillis * errorCount) + + (((long)options.backoffExponentialMillis) << shift); + + // Treat backoff like a moratorium: don't let the backoff + // time grow too large. + if (moratoriumTimeMillis > 0 && backoff > moratoriumTimeMillis) { + backoff = moratoriumTimeMillis; + } + + time = Math.max(time, lastErrorTimeMillis + backoff); } return time; } diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java index 955508f..87e2cd8 100644 --- a/common/tests/src/com/android/common/OperationSchedulerTest.java +++ b/common/tests/src/com/android/common/OperationSchedulerTest.java @@ -119,6 +119,42 @@ public class OperationSchedulerTest extends AndroidTestCase { assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options)); } + @MediumTest + public void testExponentialBackoff() throws Exception { + TimeTravelScheduler scheduler = new TimeTravelScheduler(); + OperationScheduler.Options options = new OperationScheduler.Options(); + options.backoffFixedMillis = 100; + options.backoffIncrementalMillis = 1000; + options.backoffExponentialMillis = 10000; + scheduler.setTriggerTimeMillis(0); + scheduler.setEnabledState(true); + + // Backoff interval after an error + long beforeError = (scheduler.timeMillis += 10); + scheduler.onTransientError(); + assertEquals(0, scheduler.getLastSuccessTimeMillis()); + assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); + assertEquals(beforeError + 11100, scheduler.getNextTimeMillis(options)); + + // Second error + beforeError = (scheduler.timeMillis += 10); + scheduler.onTransientError(); + assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); + assertEquals(beforeError + 22100, scheduler.getNextTimeMillis(options)); + + // Third error + beforeError = (scheduler.timeMillis += 10); + scheduler.onTransientError(); + assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); + assertEquals(beforeError + 43100, scheduler.getNextTimeMillis(options)); + + // Fourth error + beforeError = (scheduler.timeMillis += 10); + scheduler.onTransientError(); + assertEquals(beforeError, scheduler.getLastAttemptTimeMillis()); + assertEquals(beforeError + 84100, scheduler.getNextTimeMillis(options)); + } + @SmallTest public void testParseOptions() throws Exception { OperationScheduler.Options options = new OperationScheduler.Options(); @@ -138,6 +174,10 @@ public class OperationSchedulerTest extends AndroidTestCase { assertEquals( "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]", OperationScheduler.parseOptions("", options).toString()); + + assertEquals( + "OperationScheduler.Options[backoff=5.0+2.5+10.0 max=12345.6 min=7.0 period=3600.0]", + OperationScheduler.parseOptions("backoff=5.0++10.0 3600", options).toString()); } @SmallTest diff --git a/photoviewer/.gitignore b/photoviewer/.gitignore new file mode 100644 index 0000000..ff7ef7d --- /dev/null +++ b/photoviewer/.gitignore @@ -0,0 +1,8 @@ +*~ +*.bak +*.class +bin/ +gen/ +*.properties +.classpath +.project diff --git a/photoviewer/Android.mk b/photoviewer/Android.mk deleted file mode 100644 index 54ad563..0000000 --- a/photoviewer/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2012 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := android-common-photoviewer - -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 - -LOCAL_SDK_VERSION := 16 -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-logtags-files-under, src) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -include $(BUILD_STATIC_JAVA_LIBRARY) - -################################################## -# Build all sub-directories - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/photoviewer/AndroidManifest.xml b/photoviewer/AndroidManifest.xml deleted file mode 100644 index 485e044..0000000 --- a/photoviewer/AndroidManifest.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.ex.photo" - android:versionCode="1"> -</manifest>
\ No newline at end of file diff --git a/photoviewer/res/drawable-hdpi/actionbar_translucent.9.png b/photoviewer/res/drawable-hdpi/actionbar_translucent.9.png Binary files differdeleted file mode 100644 index f18761f..0000000 --- a/photoviewer/res/drawable-hdpi/actionbar_translucent.9.png +++ /dev/null diff --git a/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png Binary files differdeleted file mode 100644 index 69ac31b..0000000 --- a/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png +++ /dev/null diff --git a/photoviewer/res/drawable-mdpi/actionbar_translucent.9.png b/photoviewer/res/drawable-mdpi/actionbar_translucent.9.png Binary files differdeleted file mode 100644 index f78fb8a..0000000 --- a/photoviewer/res/drawable-mdpi/actionbar_translucent.9.png +++ /dev/null diff --git a/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png Binary files differdeleted file mode 100644 index f68aacf..0000000 --- a/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png +++ /dev/null diff --git a/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png Binary files differdeleted file mode 100644 index 3db90ee..0000000 --- a/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png +++ /dev/null diff --git a/photoviewer/res/drawable/default_image.png b/photoviewer/res/drawable/default_image.png Binary files differdeleted file mode 100644 index f6e6e60..0000000 --- a/photoviewer/res/drawable/default_image.png +++ /dev/null diff --git a/photoviewer/res/layout/photo_activity_view.xml b/photoviewer/res/layout/photo_activity_view.xml deleted file mode 100644 index e273140..0000000 --- a/photoviewer/res/layout/photo_activity_view.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2011 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/photo_activity_root_view" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <com.android.ex.photo.PhotoViewPager - android:id="@+id/photo_view_pager" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - -</FrameLayout> diff --git a/photoviewer/res/layout/photo_fragment_view.xml b/photoviewer/res/layout/photo_fragment_view.xml deleted file mode 100644 index 3dea9f1..0000000 --- a/photoviewer/res/layout/photo_fragment_view.xml +++ /dev/null @@ -1,82 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2011 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <com.android.ex.photo.views.PhotoView - android:id="@+id/photo_view" - android:layout_width="match_parent" - android:layout_height="match_parent" /> - - <FrameLayout - android:id="@+id/photo_preview" - android:layout_width="@dimen/photo_preview_size" - android:layout_height="@dimen/photo_preview_size" - android:layout_centerInParent="true" > - - <ImageView - android:id="@+id/photo_preview_image" - android:layout_width="@dimen/photo_preview_size" - android:layout_height="@dimen/photo_preview_size" - android:layout_gravity="center" - android:scaleType="centerCrop" - android:src="@drawable/default_image" - android:visibility="gone" /> - - <ProgressBar - android:id="@+id/indeterminate_progress" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="@dimen/photo_preview_size" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:indeterminate="true" - android:visibility="gone" /> - - <ProgressBar - android:id="@+id/determinate_progress" - style="?android:attr/progressBarStyleHorizontal" - android:layout_width="@dimen/photo_preview_size" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:indeterminate="false" - android:visibility="gone" /> - </FrameLayout> - - <TextView - android:id="@+id/empty_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/photo_preview" - android:layout_centerHorizontal="true" - android:layout_marginTop="8dip" - android:textColor="@android:color/white" - android:visibility="gone" /> - - <ImageView - android:id="@+id/retry_button" - android:layout_width="@dimen/retry_button_size" - android:layout_height="@dimen/retry_button_size" - android:layout_below="@id/empty_text" - android:layout_centerHorizontal="true" - android:background="?android:attr/selectableItemBackground" - android:scaleType="center" - android:src="@drawable/ic_menu_refresh_holo_dark" - android:visibility="gone" /> - -</RelativeLayout> diff --git a/photoviewer/res/values-af/strings.xml b/photoviewer/res/values-af/strings.xml deleted file mode 100644 index 9c3ca85..0000000 --- a/photoviewer/res/values-af/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> van <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Herprobeer"</string> -</resources> diff --git a/photoviewer/res/values-am/strings.xml b/photoviewer/res/values-am/strings.xml deleted file mode 100644 index e1506bb..0000000 --- a/photoviewer/res/values-am/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> ከ<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"እንደገና ሞክር"</string> -</resources> diff --git a/photoviewer/res/values-ar/strings.xml b/photoviewer/res/values-ar/strings.xml deleted file mode 100644 index d98e088..0000000 --- a/photoviewer/res/values-ar/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> من <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"إعادة المحاولة"</string> -</resources> diff --git a/photoviewer/res/values-be/strings.xml b/photoviewer/res/values-be/strings.xml deleted file mode 100644 index 93304c7..0000000 --- a/photoviewer/res/values-be/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> з <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Паўтарыць спробу"</string> -</resources> diff --git a/photoviewer/res/values-bg/strings.xml b/photoviewer/res/values-bg/strings.xml deleted file mode 100644 index 58d4a2b..0000000 --- a/photoviewer/res/values-bg/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> от <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Нов опит"</string> -</resources> diff --git a/photoviewer/res/values-ca/strings.xml b/photoviewer/res/values-ca/strings.xml deleted file mode 100644 index 4d46071..0000000 --- a/photoviewer/res/values-ca/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> de <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Torna-ho a provar"</string> -</resources> diff --git a/photoviewer/res/values-cs/strings.xml b/photoviewer/res/values-cs/strings.xml deleted file mode 100644 index 0d51d1b..0000000 --- a/photoviewer/res/values-cs/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> z <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Opakovat"</string> -</resources> diff --git a/photoviewer/res/values-da/strings.xml b/photoviewer/res/values-da/strings.xml deleted file mode 100644 index fc9dd30..0000000 --- a/photoviewer/res/values-da/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> af <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Forsøg igen"</string> -</resources> diff --git a/photoviewer/res/values-de/strings.xml b/photoviewer/res/values-de/strings.xml deleted file mode 100644 index 14bb59c..0000000 --- a/photoviewer/res/values-de/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> von <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Wiederholen"</string> -</resources> diff --git a/photoviewer/res/values-el/strings.xml b/photoviewer/res/values-el/strings.xml deleted file mode 100644 index 3292172..0000000 --- a/photoviewer/res/values-el/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> από <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Επανάληψη"</string> -</resources> diff --git a/photoviewer/res/values-en-rGB/strings.xml b/photoviewer/res/values-en-rGB/strings.xml deleted file mode 100644 index c6e5289..0000000 --- a/photoviewer/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> of <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Retry"</string> -</resources> diff --git a/photoviewer/res/values-es-rUS/strings.xml b/photoviewer/res/values-es-rUS/strings.xml deleted file mode 100644 index ffa0414..0000000 --- a/photoviewer/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> de <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Intentar nuevamente"</string> -</resources> diff --git a/photoviewer/res/values-es/strings.xml b/photoviewer/res/values-es/strings.xml deleted file mode 100644 index 9fa6031..0000000 --- a/photoviewer/res/values-es/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> de <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Volver a intentar"</string> -</resources> diff --git a/photoviewer/res/values-et/strings.xml b/photoviewer/res/values-et/strings.xml deleted file mode 100644 index 888c1f9..0000000 --- a/photoviewer/res/values-et/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g>/<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Proovi uuesti"</string> -</resources> diff --git a/photoviewer/res/values-fa/strings.xml b/photoviewer/res/values-fa/strings.xml deleted file mode 100644 index 5ebd26b..0000000 --- a/photoviewer/res/values-fa/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> از <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"سعی مجدد"</string> -</resources> diff --git a/photoviewer/res/values-fi/strings.xml b/photoviewer/res/values-fi/strings.xml deleted file mode 100644 index ea9ccc8..0000000 --- a/photoviewer/res/values-fi/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g>/<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Yritä uudelleen"</string> -</resources> diff --git a/photoviewer/res/values-fr/strings.xml b/photoviewer/res/values-fr/strings.xml deleted file mode 100644 index 6a55c27..0000000 --- a/photoviewer/res/values-fr/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> sur <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Réessayer"</string> -</resources> diff --git a/photoviewer/res/values-hi/strings.xml b/photoviewer/res/values-hi/strings.xml deleted file mode 100644 index 54d9612..0000000 --- a/photoviewer/res/values-hi/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="COUNT">%d</xliff:g> में से <xliff:g id="CURRENT_POS">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"पुनः प्रयास करें"</string> -</resources> diff --git a/photoviewer/res/values-hr/strings.xml b/photoviewer/res/values-hr/strings.xml deleted file mode 100644 index e10f61a..0000000 --- a/photoviewer/res/values-hr/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> od <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Pokušaj ponovo"</string> -</resources> diff --git a/photoviewer/res/values-hu/strings.xml b/photoviewer/res/values-hu/strings.xml deleted file mode 100644 index bb5afbe..0000000 --- a/photoviewer/res/values-hu/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="COUNT">%d</xliff:g>/<xliff:g id="CURRENT_POS">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Újra"</string> -</resources> diff --git a/photoviewer/res/values-in/strings.xml b/photoviewer/res/values-in/strings.xml deleted file mode 100644 index d982638..0000000 --- a/photoviewer/res/values-in/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> dari <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Coba lagi"</string> -</resources> diff --git a/photoviewer/res/values-it/strings.xml b/photoviewer/res/values-it/strings.xml deleted file mode 100644 index 77bf13a..0000000 --- a/photoviewer/res/values-it/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> di <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Riprova"</string> -</resources> diff --git a/photoviewer/res/values-iw/strings.xml b/photoviewer/res/values-iw/strings.xml deleted file mode 100644 index 450b159..0000000 --- a/photoviewer/res/values-iw/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> מתוך <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"נסה שנית"</string> -</resources> diff --git a/photoviewer/res/values-ja/strings.xml b/photoviewer/res/values-ja/strings.xml deleted file mode 100644 index 0362113..0000000 --- a/photoviewer/res/values-ja/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g>/<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"再試行"</string> -</resources> diff --git a/photoviewer/res/values-ko/strings.xml b/photoviewer/res/values-ko/strings.xml deleted file mode 100644 index 150112d..0000000 --- a/photoviewer/res/values-ko/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g>/<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"다시 시도"</string> -</resources> diff --git a/photoviewer/res/values-lt/strings.xml b/photoviewer/res/values-lt/strings.xml deleted file mode 100644 index 7c32566..0000000 --- a/photoviewer/res/values-lt/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> iš <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Bandyti dar kartą"</string> -</resources> diff --git a/photoviewer/res/values-lv/strings.xml b/photoviewer/res/values-lv/strings.xml deleted file mode 100644 index 0857782..0000000 --- a/photoviewer/res/values-lv/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g>. no <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Mēģināt vēlreiz"</string> -</resources> diff --git a/photoviewer/res/values-ms/strings.xml b/photoviewer/res/values-ms/strings.xml deleted file mode 100644 index ec0d23a..0000000 --- a/photoviewer/res/values-ms/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> daripada <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Cuba semula"</string> -</resources> diff --git a/photoviewer/res/values-nb/strings.xml b/photoviewer/res/values-nb/strings.xml deleted file mode 100644 index b533fc9..0000000 --- a/photoviewer/res/values-nb/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> av <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Prøv på nytt"</string> -</resources> diff --git a/photoviewer/res/values-nl/strings.xml b/photoviewer/res/values-nl/strings.xml deleted file mode 100644 index e61b149..0000000 --- a/photoviewer/res/values-nl/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> van <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Opnieuw proberen"</string> -</resources> diff --git a/photoviewer/res/values-pl/strings.xml b/photoviewer/res/values-pl/strings.xml deleted file mode 100644 index fe33696..0000000 --- a/photoviewer/res/values-pl/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> z <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Spróbuj ponownie"</string> -</resources> diff --git a/photoviewer/res/values-pt-rPT/strings.xml b/photoviewer/res/values-pt-rPT/strings.xml deleted file mode 100644 index cc62c4a..0000000 --- a/photoviewer/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> de <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Tentar novamente"</string> -</resources> diff --git a/photoviewer/res/values-pt/strings.xml b/photoviewer/res/values-pt/strings.xml deleted file mode 100644 index cc62c4a..0000000 --- a/photoviewer/res/values-pt/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> de <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Tentar novamente"</string> -</resources> diff --git a/photoviewer/res/values-ro/strings.xml b/photoviewer/res/values-ro/strings.xml deleted file mode 100644 index 6c4b724..0000000 --- a/photoviewer/res/values-ro/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> din <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Reîncercaţi"</string> -</resources> diff --git a/photoviewer/res/values-ru/strings.xml b/photoviewer/res/values-ru/strings.xml deleted file mode 100644 index 590adbc..0000000 --- a/photoviewer/res/values-ru/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> из <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Повторить"</string> -</resources> diff --git a/photoviewer/res/values-sk/strings.xml b/photoviewer/res/values-sk/strings.xml deleted file mode 100644 index a9f6616..0000000 --- a/photoviewer/res/values-sk/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> z <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Skúsiť znovu"</string> -</resources> diff --git a/photoviewer/res/values-sl/strings.xml b/photoviewer/res/values-sl/strings.xml deleted file mode 100644 index 816b946..0000000 --- a/photoviewer/res/values-sl/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> od <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Znova"</string> -</resources> diff --git a/photoviewer/res/values-sr/strings.xml b/photoviewer/res/values-sr/strings.xml deleted file mode 100644 index 43fb7b1..0000000 --- a/photoviewer/res/values-sr/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> од <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Покушај поново"</string> -</resources> diff --git a/photoviewer/res/values-sv/strings.xml b/photoviewer/res/values-sv/strings.xml deleted file mode 100644 index 8eef180..0000000 --- a/photoviewer/res/values-sv/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> av <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Försök igen"</string> -</resources> diff --git a/photoviewer/res/values-sw/strings.xml b/photoviewer/res/values-sw/strings.xml deleted file mode 100644 index 3253357..0000000 --- a/photoviewer/res/values-sw/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> ya <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Jaribu tena"</string> -</resources> diff --git a/photoviewer/res/values-th/strings.xml b/photoviewer/res/values-th/strings.xml deleted file mode 100644 index de409e8..0000000 --- a/photoviewer/res/values-th/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> จาก <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"ลองอีกครั้ง"</string> -</resources> diff --git a/photoviewer/res/values-tl/strings.xml b/photoviewer/res/values-tl/strings.xml deleted file mode 100644 index 40b4a77..0000000 --- a/photoviewer/res/values-tl/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> ng <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Subukang muli"</string> -</resources> diff --git a/photoviewer/res/values-tr/strings.xml b/photoviewer/res/values-tr/strings.xml deleted file mode 100644 index 0b2b9e6..0000000 --- a/photoviewer/res/values-tr/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> / <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Yeniden dene"</string> -</resources> diff --git a/photoviewer/res/values-uk/strings.xml b/photoviewer/res/values-uk/strings.xml deleted file mode 100644 index 77b3c08..0000000 --- a/photoviewer/res/values-uk/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> з <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Повторити"</string> -</resources> diff --git a/photoviewer/res/values-vi/strings.xml b/photoviewer/res/values-vi/strings.xml deleted file mode 100644 index 851d793..0000000 --- a/photoviewer/res/values-vi/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> trong số <xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Thử lại"</string> -</resources> diff --git a/photoviewer/res/values-zh-rCN/strings.xml b/photoviewer/res/values-zh-rCN/strings.xml deleted file mode 100644 index 65d8b03..0000000 --- a/photoviewer/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"第 <xliff:g id="CURRENT_POS">%d</xliff:g> 张,共 <xliff:g id="COUNT">%d</xliff:g> 张"</string> - <string name="retry" msgid="3319517143224679074">"重试"</string> -</resources> diff --git a/photoviewer/res/values-zh-rTW/strings.xml b/photoviewer/res/values-zh-rTW/strings.xml deleted file mode 100644 index fa555d0..0000000 --- a/photoviewer/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"第 <xliff:g id="CURRENT_POS">%d</xliff:g> 張 (共 <xliff:g id="COUNT">%d</xliff:g> 張)"</string> - <string name="retry" msgid="3319517143224679074">"重試"</string> -</resources> diff --git a/photoviewer/res/values-zu/strings.xml b/photoviewer/res/values-zu/strings.xml deleted file mode 100644 index 31aec64..0000000 --- a/photoviewer/res/values-zu/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="photo_view_count" msgid="3466690572899800275">"<xliff:g id="CURRENT_POS">%d</xliff:g> kwe-<xliff:g id="COUNT">%d</xliff:g>"</string> - <string name="retry" msgid="3319517143224679074">"Zama futhi"</string> -</resources> diff --git a/photoviewer/res/values/colors.xml b/photoviewer/res/values/colors.xml deleted file mode 100644 index cd2a14b..0000000 --- a/photoviewer/res/values/colors.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2011 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- Photo Viewer Colors --> - <color name="solid_black">#ff000000</color> - <color name="photo_crop_dim_color">#cc000000</color> - <color name="photo_crop_highlight_color">#fff</color> -</resources> diff --git a/photoviewer/res/values/constants.xml b/photoviewer/res/values/constants.xml deleted file mode 100644 index ae480ce..0000000 --- a/photoviewer/res/values/constants.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <integer name="action_bar_delay_time_in_millis">5000</integer> -</resources> diff --git a/photoviewer/res/values/dimen.xml b/photoviewer/res/values/dimen.xml deleted file mode 100644 index 754c99b..0000000 --- a/photoviewer/res/values/dimen.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <dimen name="photo_crop_width">280dip</dimen> - <dimen name="photo_crop_stroke_width">1dip</dimen> - <dimen name="photo_preview_size">200dip</dimen> - <dimen name="retry_button_size">48dip</dimen> - <dimen name="photo_page_margin">32dip</dimen> -</resources> diff --git a/photoviewer/res/values/strings.xml b/photoviewer/res/values/strings.xml deleted file mode 100644 index b635122..0000000 --- a/photoviewer/res/values/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Photo view sub-title for current photo position [CHAR LIMIT=10] --> - <string name="photo_view_count"><xliff:g id="current_pos">%d</xliff:g> of <xliff:g id="count">%d</xliff:g></string> - <string name="retry">Retry</string> -</resources>
\ No newline at end of file diff --git a/photoviewer/res/values/themes.xml b/photoviewer/res/values/themes.xml deleted file mode 100644 index 04bec53..0000000 --- a/photoviewer/res/values/themes.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2012 Google Inc. - Licensed to The Android Open Source Project. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <style name="PhotoViewTheme" parent="android:Theme.Holo"> - <item name="android:windowNoTitle">false</item> - <item name="android:windowContentOverlay">@null</item> - <item name="android:windowActionBarOverlay">true</item> - <item name="android:windowBackground">@color/solid_black</item> - <item name="android:actionBarStyle">@style/Holo.ActionBar</item> - </style> - <style name="Holo.ActionBar" parent="android:Widget.Holo.ActionBar"> - <item name="android:background">@drawable/actionbar_translucent</item> - </style> -</resources> diff --git a/photoviewer/sample/Android.mk b/photoviewer/sample/Android.mk deleted file mode 100644 index 8346e7c..0000000 --- a/photoviewer/sample/Android.mk +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2011, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -#Include res dir from photoviewer -photo_dir := ..//res -res_dirs := $(photo_dir) res - -################################################## -# Build APK -include $(CLEAR_VARS) - -src_dirs := src -LOCAL_PACKAGE_NAME := PhotoViewerSample - -LOCAL_STATIC_JAVA_LIBRARIES += android-common-photoviewer -LOCAL_STATIC_JAVA_LIBRARIES += android-common -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 -LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13 - -LOCAL_SDK_VERSION := 16 - -LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs)) \ - $(call all-logtags-files-under, $(src_dirs)) -LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) $(LOCAL_PATH)/res -LOCAL_AAPT_FLAGS := --auto-add-overlay -LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.photo - -include $(BUILD_PACKAGE) - - -################################################## -# Build all sub-directories - -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/photoviewer/sample/AndroidManifest.xml b/photoviewer/sample/AndroidManifest.xml deleted file mode 100644 index 8a75d6a..0000000 --- a/photoviewer/sample/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.photoviewersample" - android:versionCode="1" - android:versionName="1.0" > - - <uses-sdk - android:minSdkVersion="14" - android:targetSdkVersion="16" /> - - <application - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" - android:theme="@style/AppTheme" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - <activity - android:name="com.android.ex.photo.PhotoViewActivity" - android:label="@string/app_name" - android:theme="@style/PhotoViewTheme" > - </activity> - <provider - android:name=".SampleProvider" - android:exported="false" - android:authorities="com.example.photoviewersample.SampleProvider"> - </provider> - </application> - -</manifest>
\ No newline at end of file diff --git a/photoviewer/sample/assets/blah.png b/photoviewer/sample/assets/blah.png Binary files differdeleted file mode 100644 index f895669..0000000 --- a/photoviewer/sample/assets/blah.png +++ /dev/null diff --git a/photoviewer/sample/assets/galaxy.png b/photoviewer/sample/assets/galaxy.png Binary files differdeleted file mode 100644 index 3fcfec8..0000000 --- a/photoviewer/sample/assets/galaxy.png +++ /dev/null diff --git a/photoviewer/sample/assets/johannson.png b/photoviewer/sample/assets/johannson.png Binary files differdeleted file mode 100644 index 0b6be7f..0000000 --- a/photoviewer/sample/assets/johannson.png +++ /dev/null diff --git a/photoviewer/sample/assets/planets.png b/photoviewer/sample/assets/planets.png Binary files differdeleted file mode 100644 index 1644c48..0000000 --- a/photoviewer/sample/assets/planets.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-hdpi/ic_action_search.png b/photoviewer/sample/res/drawable-hdpi/ic_action_search.png Binary files differdeleted file mode 100644 index 67de12d..0000000 --- a/photoviewer/sample/res/drawable-hdpi/ic_action_search.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-hdpi/ic_launcher.png b/photoviewer/sample/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index fba1ff0..0000000 --- a/photoviewer/sample/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-mdpi/ic_action_search.png b/photoviewer/sample/res/drawable-mdpi/ic_action_search.png Binary files differdeleted file mode 100644 index 134d549..0000000 --- a/photoviewer/sample/res/drawable-mdpi/ic_action_search.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-mdpi/ic_launcher.png b/photoviewer/sample/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 72a445d..0000000 --- a/photoviewer/sample/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-xhdpi/ic_action_search.png b/photoviewer/sample/res/drawable-xhdpi/ic_action_search.png Binary files differdeleted file mode 100644 index d699c6b..0000000 --- a/photoviewer/sample/res/drawable-xhdpi/ic_action_search.png +++ /dev/null diff --git a/photoviewer/sample/res/drawable-xhdpi/ic_launcher.png b/photoviewer/sample/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 002e7b0..0000000 --- a/photoviewer/sample/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/photoviewer/sample/res/layout/activity_main.xml b/photoviewer/sample/res/layout/activity_main.xml deleted file mode 100644 index ae94392..0000000 --- a/photoviewer/sample/res/layout/activity_main.xml +++ /dev/null @@ -1,16 +0,0 @@ -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <Button - android:id="@+id/button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:layout_centerVertical="true" - android:padding="@dimen/padding_medium" - android:text="@string/launch" - tools:context=".MainActivity" /> - -</RelativeLayout> diff --git a/photoviewer/sample/res/menu/activity_main.xml b/photoviewer/sample/res/menu/activity_main.xml deleted file mode 100644 index cfc10fd..0000000 --- a/photoviewer/sample/res/menu/activity_main.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/menu_settings" - android:title="@string/menu_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/photoviewer/sample/res/values-af/strings.xml b/photoviewer/sample/res/values-af/strings.xml deleted file mode 100644 index 952a3b5..0000000 --- a/photoviewer/sample/res/values-af/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Instellings"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Begin fotokyker"</string> -</resources> diff --git a/photoviewer/sample/res/values-am/strings.xml b/photoviewer/sample/res/values-am/strings.xml deleted file mode 100644 index 0d4989a..0000000 --- a/photoviewer/sample/res/values-am/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"ቅንብሮች"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"ፎቶ ተመልካችን አስነሳ"</string> -</resources> diff --git a/photoviewer/sample/res/values-ar/strings.xml b/photoviewer/sample/res/values-ar/strings.xml deleted file mode 100644 index 8f71f28..0000000 --- a/photoviewer/sample/res/values-ar/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"الإعدادات"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"تشغيل عارض الصور"</string> -</resources> diff --git a/photoviewer/sample/res/values-be/strings.xml b/photoviewer/sample/res/values-be/strings.xml deleted file mode 100644 index d52e8ad..0000000 --- a/photoviewer/sample/res/values-be/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Налады"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Запусцiць Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-bg/strings.xml b/photoviewer/sample/res/values-bg/strings.xml deleted file mode 100644 index 37d2ea7..0000000 --- a/photoviewer/sample/res/values-bg/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Настройки"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Стартиране на Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-ca/strings.xml b/photoviewer/sample/res/values-ca/strings.xml deleted file mode 100644 index 11ca3ff..0000000 --- a/photoviewer/sample/res/values-ca/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Mostra del lector de fotos"</string> - <string name="menu_settings" msgid="1259682084875185697">"Configuració"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Activitat principal"</string> - <string name="launch" msgid="2345080120370773520">"Inicia el lector de fotos"</string> -</resources> diff --git a/photoviewer/sample/res/values-cs/strings.xml b/photoviewer/sample/res/values-cs/strings.xml deleted file mode 100644 index 8f222fc..0000000 --- a/photoviewer/sample/res/values-cs/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Příklad prohlížeče fotografií"</string> - <string name="menu_settings" msgid="1259682084875185697">"Nastavení"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Hlavní aktivita"</string> - <string name="launch" msgid="2345080120370773520">"Spustit prohlížeč fotografií"</string> -</resources> diff --git a/photoviewer/sample/res/values-da/strings.xml b/photoviewer/sample/res/values-da/strings.xml deleted file mode 100644 index 4a8d3c7..0000000 --- a/photoviewer/sample/res/values-da/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Indstillinger"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Start fotofremviser"</string> -</resources> diff --git a/photoviewer/sample/res/values-de/strings.xml b/photoviewer/sample/res/values-de/strings.xml deleted file mode 100644 index 03f1ba6..0000000 --- a/photoviewer/sample/res/values-de/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Einstellungen"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Photo Viewer starten"</string> -</resources> diff --git a/photoviewer/sample/res/values-el/strings.xml b/photoviewer/sample/res/values-el/strings.xml deleted file mode 100644 index 6e5ec4a..0000000 --- a/photoviewer/sample/res/values-el/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Ρυθμίσεις"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Εκκίνηση του Προγράμματος προβολής φωτογραφιών"</string> -</resources> diff --git a/photoviewer/sample/res/values-en-rGB/strings.xml b/photoviewer/sample/res/values-en-rGB/strings.xml deleted file mode 100644 index 66a0898..0000000 --- a/photoviewer/sample/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Settings"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Launch Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-es-rUS/strings.xml b/photoviewer/sample/res/values-es-rUS/strings.xml deleted file mode 100644 index ab4e5a0..0000000 --- a/photoviewer/sample/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Configuración"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Actividad principal"</string> - <string name="launch" msgid="2345080120370773520">"Iniciar Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-es/strings.xml b/photoviewer/sample/res/values-es/strings.xml deleted file mode 100644 index 8af0dbc..0000000 --- a/photoviewer/sample/res/values-es/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Ejemplo de Photo Viewer"</string> - <string name="menu_settings" msgid="1259682084875185697">"Ajustes"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Actividad principal"</string> - <string name="launch" msgid="2345080120370773520">"Iniciar Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-et/strings.xml b/photoviewer/sample/res/values-et/strings.xml deleted file mode 100644 index 9532a25..0000000 --- a/photoviewer/sample/res/values-et/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Seaded"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Fotovaaturi käivitamine"</string> -</resources> diff --git a/photoviewer/sample/res/values-fa/strings.xml b/photoviewer/sample/res/values-fa/strings.xml deleted file mode 100644 index ea17cdb..0000000 --- a/photoviewer/sample/res/values-fa/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"نمونه نمایشگر تصویر"</string> - <string name="menu_settings" msgid="1259682084875185697">"تنظیمات"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"راهاندازی نمایشگر تصویر"</string> -</resources> diff --git a/photoviewer/sample/res/values-fi/strings.xml b/photoviewer/sample/res/values-fi/strings.xml deleted file mode 100644 index 7bd9359..0000000 --- a/photoviewer/sample/res/values-fi/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Asetukset"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Käynnistä Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-fr/strings.xml b/photoviewer/sample/res/values-fr/strings.xml deleted file mode 100644 index 6e3cf79..0000000 --- a/photoviewer/sample/res/values-fr/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Paramètres"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Lancer la visionneuse de photos"</string> -</resources> diff --git a/photoviewer/sample/res/values-hi/strings.xml b/photoviewer/sample/res/values-hi/strings.xml deleted file mode 100644 index 33f30b0..0000000 --- a/photoviewer/sample/res/values-hi/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"सेटिंग"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"फ़ोटो व्यूअर लॉन्च करें"</string> -</resources> diff --git a/photoviewer/sample/res/values-hr/strings.xml b/photoviewer/sample/res/values-hr/strings.xml deleted file mode 100644 index c5f11d0..0000000 --- a/photoviewer/sample/res/values-hr/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Postavke"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Pokreni Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-hu/strings.xml b/photoviewer/sample/res/values-hu/strings.xml deleted file mode 100644 index 6a38dba..0000000 --- a/photoviewer/sample/res/values-hu/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Beállítások"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"A Photo Viewer elindítása"</string> -</resources> diff --git a/photoviewer/sample/res/values-in/strings.xml b/photoviewer/sample/res/values-in/strings.xml deleted file mode 100644 index ac1e887..0000000 --- a/photoviewer/sample/res/values-in/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Setelan"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Luncurkan Penampil Foto"</string> -</resources> diff --git a/photoviewer/sample/res/values-it/strings.xml b/photoviewer/sample/res/values-it/strings.xml deleted file mode 100644 index 7c37c68..0000000 --- a/photoviewer/sample/res/values-it/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Impostazioni"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Avvia Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-iw/strings.xml b/photoviewer/sample/res/values-iw/strings.xml deleted file mode 100644 index 5087894..0000000 --- a/photoviewer/sample/res/values-iw/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"הגדרות"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"הפעל את מציג התמונות"</string> -</resources> diff --git a/photoviewer/sample/res/values-ja/strings.xml b/photoviewer/sample/res/values-ja/strings.xml deleted file mode 100644 index d5bdf48..0000000 --- a/photoviewer/sample/res/values-ja/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"設定"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"フォトビューアを起動"</string> -</resources> diff --git a/photoviewer/sample/res/values-ko/strings.xml b/photoviewer/sample/res/values-ko/strings.xml deleted file mode 100644 index 0366b9e..0000000 --- a/photoviewer/sample/res/values-ko/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"사진 뷰어 샘플"</string> - <string name="menu_settings" msgid="1259682084875185697">"설정"</string> - <string name="title_activity_main" msgid="7695239211427024237">"주요 활동"</string> - <string name="launch" msgid="2345080120370773520">"사진 뷰어 실행"</string> -</resources> diff --git a/photoviewer/sample/res/values-lt/strings.xml b/photoviewer/sample/res/values-lt/strings.xml deleted file mode 100644 index fe66735..0000000 --- a/photoviewer/sample/res/values-lt/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Nuotraukų peržiūros priemonės pavyzdys"</string> - <string name="menu_settings" msgid="1259682084875185697">"Nustatymai"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Pagrindinė veikla"</string> - <string name="launch" msgid="2345080120370773520">"Paleisti nuotraukų peržiūros priemonę"</string> -</resources> diff --git a/photoviewer/sample/res/values-lv/strings.xml b/photoviewer/sample/res/values-lv/strings.xml deleted file mode 100644 index 43ffeec..0000000 --- a/photoviewer/sample/res/values-lv/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Fotoattēlu skatītāja paraugs"</string> - <string name="menu_settings" msgid="1259682084875185697">"Iestatījumi"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Galvenās darbības"</string> - <string name="launch" msgid="2345080120370773520">"Palaist fotoattēlu skatītāju"</string> -</resources> diff --git a/photoviewer/sample/res/values-ms/strings.xml b/photoviewer/sample/res/values-ms/strings.xml deleted file mode 100644 index 8ad0bca..0000000 --- a/photoviewer/sample/res/values-ms/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Tetapan"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Aktiviti Utama"</string> - <string name="launch" msgid="2345080120370773520">"Lancarkan Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-nb/strings.xml b/photoviewer/sample/res/values-nb/strings.xml deleted file mode 100644 index 85e04e6..0000000 --- a/photoviewer/sample/res/values-nb/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Innstillinger"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Start Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-nl/strings.xml b/photoviewer/sample/res/values-nl/strings.xml deleted file mode 100644 index 6a4b531..0000000 --- a/photoviewer/sample/res/values-nl/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Instellingen"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Fotoviewer starten"</string> -</resources> diff --git a/photoviewer/sample/res/values-pl/strings.xml b/photoviewer/sample/res/values-pl/strings.xml deleted file mode 100644 index 282bde1..0000000 --- a/photoviewer/sample/res/values-pl/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Ustawienia"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Uruchom przeglądarkę zdjęć"</string> -</resources> diff --git a/photoviewer/sample/res/values-pt-rPT/strings.xml b/photoviewer/sample/res/values-pt-rPT/strings.xml deleted file mode 100644 index 46f8f28..0000000 --- a/photoviewer/sample/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"ExemploVisualizadorFotos"</string> - <string name="menu_settings" msgid="1259682084875185697">"Definições"</string> - <string name="title_activity_main" msgid="7695239211427024237">"AtividadePrincipal"</string> - <string name="launch" msgid="2345080120370773520">"Iniciar Visualizador de Fotos"</string> -</resources> diff --git a/photoviewer/sample/res/values-pt/strings.xml b/photoviewer/sample/res/values-pt/strings.xml deleted file mode 100644 index 16e146a..0000000 --- a/photoviewer/sample/res/values-pt/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"AmostraPhotoViewer"</string> - <string name="menu_settings" msgid="1259682084875185697">"Configurações"</string> - <string name="title_activity_main" msgid="7695239211427024237">"AtividadePrincipal"</string> - <string name="launch" msgid="2345080120370773520">"Iniciar o Visualizador de fotos"</string> -</resources> diff --git a/photoviewer/sample/res/values-ro/strings.xml b/photoviewer/sample/res/values-ro/strings.xml deleted file mode 100644 index 00f370e..0000000 --- a/photoviewer/sample/res/values-ro/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Setări"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Lansaţi Photo Viewer"</string> -</resources> diff --git a/photoviewer/sample/res/values-ru/strings.xml b/photoviewer/sample/res/values-ru/strings.xml deleted file mode 100644 index 2f68e2d..0000000 --- a/photoviewer/sample/res/values-ru/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Настройки"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Основные действия"</string> - <string name="launch" msgid="2345080120370773520">"Смотреть фото"</string> -</resources> diff --git a/photoviewer/sample/res/values-sk/strings.xml b/photoviewer/sample/res/values-sk/strings.xml deleted file mode 100644 index a7e5d7b..0000000 --- a/photoviewer/sample/res/values-sk/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"Príklad prehliadača fotografií"</string> - <string name="menu_settings" msgid="1259682084875185697">"Nastavenia"</string> - <string name="title_activity_main" msgid="7695239211427024237">"Hlavná aktivita"</string> - <string name="launch" msgid="2345080120370773520">"Spustiť prehliadač fotografií"</string> -</resources> diff --git a/photoviewer/sample/res/values-sl/strings.xml b/photoviewer/sample/res/values-sl/strings.xml deleted file mode 100644 index b365610..0000000 --- a/photoviewer/sample/res/values-sl/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Nastavitve"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Zaženi pregledovalnik fotografij"</string> -</resources> diff --git a/photoviewer/sample/res/values-sr/strings.xml b/photoviewer/sample/res/values-sr/strings.xml deleted file mode 100644 index ed2433f..0000000 --- a/photoviewer/sample/res/values-sr/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Подешавања"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Покрени фото прегледач"</string> -</resources> diff --git a/photoviewer/sample/res/values-sv/strings.xml b/photoviewer/sample/res/values-sv/strings.xml deleted file mode 100644 index ffd5e95..0000000 --- a/photoviewer/sample/res/values-sv/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Inställningar"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Starta fotovisaren"</string> -</resources> diff --git a/photoviewer/sample/res/values-sw/strings.xml b/photoviewer/sample/res/values-sw/strings.xml deleted file mode 100644 index 6e6b913..0000000 --- a/photoviewer/sample/res/values-sw/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"SampuliKionyeshaPicha"</string> - <string name="menu_settings" msgid="1259682084875185697">"Mipangilio"</string> - <string name="title_activity_main" msgid="7695239211427024237">"ShughuliKuu"</string> - <string name="launch" msgid="2345080120370773520">"Zindua Kionyesha Picha"</string> -</resources> diff --git a/photoviewer/sample/res/values-th/strings.xml b/photoviewer/sample/res/values-th/strings.xml deleted file mode 100644 index 1643fe2..0000000 --- a/photoviewer/sample/res/values-th/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"ตัวอย่างโปรแกรมดูรูปภาพ"</string> - <string name="menu_settings" msgid="1259682084875185697">"การตั้งค่า"</string> - <string name="title_activity_main" msgid="7695239211427024237">"กิจกรรมหลัก"</string> - <string name="launch" msgid="2345080120370773520">"เปิดโปรแกรมดูรูปภาพ"</string> -</resources> diff --git a/photoviewer/sample/res/values-tl/strings.xml b/photoviewer/sample/res/values-tl/strings.xml deleted file mode 100644 index c815b11..0000000 --- a/photoviewer/sample/res/values-tl/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Mga Setting"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Ilunsad ang Viewer ng Larawan"</string> -</resources> diff --git a/photoviewer/sample/res/values-tr/strings.xml b/photoviewer/sample/res/values-tr/strings.xml deleted file mode 100644 index 8ef780f..0000000 --- a/photoviewer/sample/res/values-tr/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Ayarlar"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Photo Viewer\'ı Başlat"</string> -</resources> diff --git a/photoviewer/sample/res/values-uk/strings.xml b/photoviewer/sample/res/values-uk/strings.xml deleted file mode 100644 index dc813ab..0000000 --- a/photoviewer/sample/res/values-uk/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Налаштування"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Запустити інструмент перегляду фотографій"</string> -</resources> diff --git a/photoviewer/sample/res/values-vi/strings.xml b/photoviewer/sample/res/values-vi/strings.xml deleted file mode 100644 index 910a52c..0000000 --- a/photoviewer/sample/res/values-vi/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Cài đặt"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Khởi chạy trình xem ảnh"</string> -</resources> diff --git a/photoviewer/sample/res/values-zh-rCN/strings.xml b/photoviewer/sample/res/values-zh-rCN/strings.xml deleted file mode 100644 index 69795a6..0000000 --- a/photoviewer/sample/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"设置"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"启动照片浏览器"</string> -</resources> diff --git a/photoviewer/sample/res/values-zh-rTW/strings.xml b/photoviewer/sample/res/values-zh-rTW/strings.xml deleted file mode 100644 index 73dd1fa..0000000 --- a/photoviewer/sample/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"設定"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"啟動相片檢視器"</string> -</resources> diff --git a/photoviewer/sample/res/values-zu/strings.xml b/photoviewer/sample/res/values-zu/strings.xml deleted file mode 100644 index e42c901..0000000 --- a/photoviewer/sample/res/values-zu/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="559800164925277094">"PhotoViewerSample"</string> - <string name="menu_settings" msgid="1259682084875185697">"Izilungiselelo"</string> - <string name="title_activity_main" msgid="7695239211427024237">"MainActivity"</string> - <string name="launch" msgid="2345080120370773520">"Vula isibonisi sesithombe"</string> -</resources> diff --git a/photoviewer/sample/res/values/dimens.xml b/photoviewer/sample/res/values/dimens.xml deleted file mode 100644 index ec96646..0000000 --- a/photoviewer/sample/res/values/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ -<resources> - - <dimen name="padding_small">8dp</dimen> - <dimen name="padding_medium">8dp</dimen> - <dimen name="padding_large">16dp</dimen> - -</resources>
\ No newline at end of file diff --git a/photoviewer/sample/res/values/strings.xml b/photoviewer/sample/res/values/strings.xml deleted file mode 100644 index be31909..0000000 --- a/photoviewer/sample/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ -<resources> - - <string name="app_name">PhotoViewerSample</string> - <string name="menu_settings">Settings</string> - <string name="title_activity_main">MainActivity</string> - <string name="launch">Launch Photo Viewer</string> - -</resources>
\ No newline at end of file diff --git a/photoviewer/sample/res/values/styles.xml b/photoviewer/sample/res/values/styles.xml deleted file mode 100644 index 1c089a7..0000000 --- a/photoviewer/sample/res/values/styles.xml +++ /dev/null @@ -1,5 +0,0 @@ -<resources> - - <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar" /> - -</resources>
\ No newline at end of file diff --git a/photoviewer/sample/src/com/example/photoviewersample/MainActivity.java b/photoviewer/sample/src/com/example/photoviewersample/MainActivity.java deleted file mode 100644 index efc2064..0000000 --- a/photoviewer/sample/src/com/example/photoviewersample/MainActivity.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.example.photoviewersample; - -import android.app.Activity; -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; - -import com.android.ex.photo.Intents; -import com.android.ex.photo.Intents.PhotoViewIntentBuilder; - -public class MainActivity extends Activity implements OnClickListener { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - View b = findViewById(R.id.button); - b.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - final PhotoViewIntentBuilder builder = - Intents.newPhotoViewActivityIntentBuilder(this); - builder - .setPhotosUri("content://com.example.photoviewersample.SampleProvider/photos"); - - startActivity(builder.build()); - } -} diff --git a/photoviewer/sample/src/com/example/photoviewersample/SampleProvider.java b/photoviewer/sample/src/com/example/photoviewersample/SampleProvider.java deleted file mode 100644 index 86ae82b..0000000 --- a/photoviewer/sample/src/com/example/photoviewersample/SampleProvider.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.example.photoviewersample; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; - -public class SampleProvider extends ContentProvider { - private static final int PHOTOS = 1; - private static final int PHOTO_INDIVIDUAL_1 = 2; - private static final int PHOTO_INDIVIDUAL_2 = 3; - private static final int PHOTO_INDIVIDUAL_3 = 4; - private static final int PHOTO_INDIVIDUAL_4 = 5; - - private static final String PROVIDER_URI = "com.example.photoviewersample.SampleProvider"; - - // Creates a UriMatcher object. - private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - - static - { - sUriMatcher.addURI(PROVIDER_URI, "photos", PHOTOS); - sUriMatcher.addURI(PROVIDER_URI, "photos/1", PHOTO_INDIVIDUAL_1); - sUriMatcher.addURI(PROVIDER_URI, "photos/2", PHOTO_INDIVIDUAL_2); - sUriMatcher.addURI(PROVIDER_URI, "photos/3", PHOTO_INDIVIDUAL_3); - sUriMatcher.addURI(PROVIDER_URI, "photos/4", PHOTO_INDIVIDUAL_4); - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public String getType(Uri uri) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean onCreate() { - // TODO Auto-generated method stub - return false; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - MatrixCursor matrix = new MatrixCursor(projection); - - /* - * Choose the table to query and a sort order based on the code returned for the incoming - * URI. Here, too, only the statements for table 3 are shown. - */ - switch (sUriMatcher.match(uri)) { - // If the incoming URI was for all of the photos table - case PHOTOS: - addRow(matrix, PHOTO_INDIVIDUAL_1); - addRow(matrix, PHOTO_INDIVIDUAL_2); - addRow(matrix, PHOTO_INDIVIDUAL_3); - addRow(matrix, PHOTO_INDIVIDUAL_4); - break; - - // If the incoming URI was for a single row - case PHOTO_INDIVIDUAL_1: - addRow(matrix, PHOTO_INDIVIDUAL_1); - break; - case PHOTO_INDIVIDUAL_2: - addRow(matrix, PHOTO_INDIVIDUAL_2); - break; - case PHOTO_INDIVIDUAL_3: - addRow(matrix, PHOTO_INDIVIDUAL_3); - break; - case PHOTO_INDIVIDUAL_4: - addRow(matrix, PHOTO_INDIVIDUAL_4); - break; - - default: - // If the URI is not recognized, you should do some error handling here. - } - // call the code to actually do the query - - return matrix; - } - - /** - * Adds a single row to the Cursor. A real implementation should - * check the projection to properly match the columns. - */ - private void addRow(MatrixCursor matrix, int match_id) { - switch (match_id) { - case PHOTO_INDIVIDUAL_1: - matrix.newRow() - .add("content://" + PROVIDER_URI + "/photos/1") // uri - .add("blah.png") // displayName - .add("content://" + PROVIDER_URI + "/photos/1/contentUri") // contentUri - .add("content://" + PROVIDER_URI + "/photos/1/thumbnailUri") // thumbnailUri - .add("image/png"); // contentType - break; - case PHOTO_INDIVIDUAL_2: - matrix.newRow() - .add("content://" + PROVIDER_URI + "/photos/2") // uri - .add("johannson.png") // displayName - .add("content://" + PROVIDER_URI + "/photos/2/contentUri") // contentUri - .add("content://" + PROVIDER_URI + "/photos/2/thumbnailUri") // thumbnailUri - .add("image/png"); // contentType - break; - case PHOTO_INDIVIDUAL_3: - matrix.newRow() - .add("content://" + PROVIDER_URI + "/photos/3") // uri - .add("planets.png") // displayName - .add("content://" + PROVIDER_URI + "/photos/3/contentUri") // contentUri - .add("content://" + PROVIDER_URI + "/photos/3/thumbnailUri") // thumbnailUri - .add("image/png"); // contentType - break; - case PHOTO_INDIVIDUAL_4: - matrix.newRow() - .add("content://" + PROVIDER_URI + "/photos/4") // uri - .add("galaxy.png") // displayName - .add("content://" + PROVIDER_URI + "/photos/4/contentUri") // contentUri - .add("content://" + PROVIDER_URI + "/photos/4/thumbnailUri") // thumbnailUri - .add("image/png"); // contentType - break; - - default: - // If the URI is not recognized, you should do some error handling here. - } - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { - List<String> pathSegments = uri.getPathSegments(); - final int id = Integer.parseInt(pathSegments.get(1)); - String fileName; - switch (id) { - case 1: - fileName = "blah.png"; - break; - case 2: - fileName = "johannson.png"; - break; - case 3: - fileName = "planets.png"; - break; - case 4: - fileName = "galaxy.png"; - break; - default: - fileName = null; - break; - } - try { - return getContext().getAssets().openFd(fileName); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/Intents.java b/photoviewer/src/com/android/ex/photo/Intents.java deleted file mode 100644 index 0e64730..0000000 --- a/photoviewer/src/com/android/ex/photo/Intents.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo; - -import android.content.ContentProvider; -import android.content.Context; -import android.content.Intent; - -import com.android.ex.photo.fragments.PhotoViewFragment; - -/** - * Build intents to start app activities - */ -public class Intents { - // Intent extras - public static final String EXTRA_PHOTO_INDEX = "photo_index"; - public static final String EXTRA_PHOTO_ID = "photo_id"; - public static final String EXTRA_PHOTOS_URI = "photos_uri"; - public static final String EXTRA_RESOLVED_PHOTO_URI = "resolved_photo_uri"; - public static final String EXTRA_PROJECTION = "projection"; - public static final String EXTRA_THUMBNAIL_URI = "thumbnail_uri"; - - /** - * Gets a photo view intent builder to display the photos from phone activity. - * - * @param context The context - * @return The intent builder - */ - public static PhotoViewIntentBuilder newPhotoViewActivityIntentBuilder(Context context) { - return new PhotoViewIntentBuilder(context, PhotoViewActivity.class); - } - - /** - * Gets a photo view intent builder to display the photo view fragment - * - * @param context The context - * @return The intent builder - */ - public static PhotoViewIntentBuilder newPhotoViewFragmentIntentBuilder(Context context) { - return new PhotoViewIntentBuilder(context, PhotoViewFragment.class); - } - - /** Gets a new photo view intent builder */ - public static PhotoViewIntentBuilder newPhotoViewIntentBuilder( - Context context, Class<? extends PhotoViewActivity> cls) { - return new PhotoViewIntentBuilder(context, cls); - } - - /** Builder to create a photo view intent */ - public static class PhotoViewIntentBuilder { - private final Intent mIntent; - - /** The index of the photo to show */ - private Integer mPhotoIndex; - /** The URI of the group of photos to display */ - private String mPhotosUri; - /** The URL of the photo to display */ - private String mResolvedPhotoUri; - /** The projection for the query to use; optional */ - private String[] mProjection; - /** The URI of a thumbnail of the photo to display */ - private String mThumbnailUri; - - private PhotoViewIntentBuilder(Context context, Class<?> cls) { - mIntent = new Intent(context, cls); - } - - /** Sets the photo index */ - public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) { - mPhotoIndex = photoIndex; - return this; - } - - /** Sets the photos URI */ - public PhotoViewIntentBuilder setPhotosUri(String photosUri) { - mPhotosUri = photosUri; - return this; - } - - /** Sets the query projection */ - public PhotoViewIntentBuilder setProjection(String[] projection) { - mProjection = projection; - return this; - } - - /** Sets the resolved photo URI. This method is for the case - * where the URI given to {@link PhotoViewActivity} points directly - * to a single image and does not need to be resolved via a query - * to the {@link ContentProvider}. If this value is set, it supersedes - * {@link #setPhotosUri(String)}. */ - public PhotoViewIntentBuilder setResolvedPhotoUri(String resolvedPhotoUri) { - mResolvedPhotoUri = resolvedPhotoUri; - return this; - } - - /** - * Sets the URI for a thumbnail preview of the photo. - */ - public PhotoViewIntentBuilder setThumbnailUri(String thumbnailUri) { - mThumbnailUri = thumbnailUri; - return this; - } - - /** Build the intent */ - public Intent build() { - mIntent.setAction(Intent.ACTION_VIEW); - - mIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - - if (mPhotoIndex != null) { - mIntent.putExtra(EXTRA_PHOTO_INDEX, (int) mPhotoIndex); - } - - if (mPhotosUri != null) { - mIntent.putExtra(EXTRA_PHOTOS_URI, mPhotosUri); - } - - if (mResolvedPhotoUri != null) { - mIntent.putExtra(EXTRA_RESOLVED_PHOTO_URI, mResolvedPhotoUri); - } - - if (mProjection != null) { - mIntent.putExtra(EXTRA_PROJECTION, mProjection); - } - - if (mThumbnailUri != null) { - mIntent.putExtra(EXTRA_THUMBNAIL_URI, mThumbnailUri); - } - - return mIntent; - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java deleted file mode 100644 index 6f126b4..0000000 --- a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo; - -import android.app.ActionBar; -import android.app.ActionBar.OnMenuVisibilityListener; -import android.app.Activity; -import android.app.ActivityManager; -import android.app.Fragment; -import android.app.LoaderManager.LoaderCallbacks; -import android.content.Intent; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.view.ViewPager.OnPageChangeListener; -import android.view.View; - -import com.android.ex.photo.PhotoViewPager.InterceptType; -import com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener; -import com.android.ex.photo.adapters.PhotoPagerAdapter; -import com.android.ex.photo.fragments.PhotoViewFragment; -import com.android.ex.photo.loaders.PhotoPagerLoader; -import com.android.ex.photo.provider.PhotoContract; - -import java.util.HashSet; -import java.util.Set; - -/** - * Activity to view the contents of an album. - */ -public class PhotoViewActivity extends Activity implements - LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener, - OnMenuVisibilityListener { - - /** - * Listener to be invoked for screen events. - */ - public static interface OnScreenListener { - - /** - * The full screen state has changed. - */ - public void onFullScreenChanged(boolean fullScreen); - - /** - * A new view has been activated and the previous view de-activated. - */ - public void onViewActivated(); - - /** - * Called when a right-to-left touch move intercept is about to occur. - * - * @param origX the raw x coordinate of the initial touch - * @param origY the raw y coordinate of the initial touch - * @return {@code true} if the touch should be intercepted. - */ - public boolean onInterceptMoveLeft(float origX, float origY); - - /** - * Called when a left-to-right touch move intercept is about to occur. - * - * @param origX the raw x coordinate of the initial touch - * @param origY the raw y coordinate of the initial touch - * @return {@code true} if the touch should be intercepted. - */ - public boolean onInterceptMoveRight(float origX, float origY); - } - - public static interface CursorChangedListener { - /** - * Called when the cursor that contains the photo list data - * is updated. Note that there is no guarantee that the cursor - * will be at the proper position. - * @param cursor the cursor containing the photo list data - */ - public void onCursorChanged(Cursor cursor); - } - - private final static String STATE_ITEM_KEY = - "com.google.android.apps.plus.PhotoViewFragment.ITEM"; - private final static String STATE_FULLSCREEN_KEY = - "com.google.android.apps.plus.PhotoViewFragment.FULLSCREEN"; - - private static final int LOADER_PHOTO_LIST = 1; - - /** Count used when the real photo count is unknown [but, may be determined] */ - public static final int ALBUM_COUNT_UNKNOWN = -1; - - /** Argument key for the dialog message */ - public static final String KEY_MESSAGE = "dialog_message"; - - public static int sMemoryClass; - - /** The URI of the photos we're viewing; may be {@code null} */ - private String mPhotosUri; - /** The index of the currently viewed photo */ - private int mPhotoIndex; - /** The query projection to use; may be {@code null} */ - private String[] mProjection; - /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */ - private int mAlbumCount = ALBUM_COUNT_UNKNOWN; - /** {@code true} if the view is empty. Otherwise, {@code false}. */ - private boolean mIsEmpty; - /** The main pager; provides left/right swipe between photos */ - private PhotoViewPager mViewPager; - /** Adapter to create pager views */ - private PhotoPagerAdapter mAdapter; - /** Whether or not we're in "full screen" mode */ - private boolean mFullScreen; - /** The set of listeners wanting full screen state */ - private Set<OnScreenListener> mScreenListeners = new HashSet<OnScreenListener>(); - /** The set of listeners wanting full screen state */ - private Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>(); - /** When {@code true}, restart the loader when the activity becomes active */ - private boolean mRestartLoader; - /** Whether or not this activity is paused */ - private boolean mIsPaused = true; - private final Handler mHandler = new Handler(); - // TODO Find a better way to do this. We basically want the activity to display the - // "loading..." progress until the fragment takes over and shows it's own "loading..." - // progress [located in photo_header_view.xml]. We could potentially have all status displayed - // by the activity, but, that gets tricky when it comes to screen rotation. For now, we - // track the loading by this variable which is fragile and may cause phantom "loading..." - // text. - private long mActionBarHideDelayTime; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - final ActivityManager mgr = (ActivityManager) getApplicationContext(). - getSystemService(Activity.ACTIVITY_SERVICE); - sMemoryClass = mgr.getMemoryClass(); - - Intent mIntent = getIntent(); - - int currentItem = -1; - if (savedInstanceState != null) { - currentItem = savedInstanceState.getInt(STATE_ITEM_KEY, -1); - mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false); - } - - // uri of the photos to view; optional - if (mIntent.hasExtra(Intents.EXTRA_PHOTOS_URI)) { - mPhotosUri = mIntent.getStringExtra(Intents.EXTRA_PHOTOS_URI); - } - - // projection for the query; optional - // I.f not set, the default projection is used. - // This projection must include the columns from the default projection. - if (mIntent.hasExtra(Intents.EXTRA_PROJECTION)) { - mProjection = mIntent.getStringArrayExtra(Intents.EXTRA_PROJECTION); - } else { - mProjection = null; - } - - // Set the current item from the intent if wasn't in the saved instance - if (mIntent.hasExtra(Intents.EXTRA_PHOTO_INDEX) && currentItem < 0) { - currentItem = mIntent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1); - } - mPhotoIndex = currentItem; - - setContentView(R.layout.photo_activity_view); - - // Create the adapter and add the view pager - mAdapter = new PhotoPagerAdapter(this, getFragmentManager(), null); - - final Resources resources = getResources(); - mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager); - mViewPager.setAdapter(mAdapter); - mViewPager.setOnPageChangeListener(this); - mViewPager.setOnInterceptTouchListener(this); - mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin)); - - // Kick off the loader - getLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this); - - final ActionBar actionBar = getActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - mActionBarHideDelayTime = resources.getInteger(R.integer.action_bar_delay_time_in_millis); - actionBar.addOnMenuVisibilityListener(this); - actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); - } - - @Override - protected void onResume() { - super.onResume(); - setFullScreen(mFullScreen, false); - - mIsPaused = false; - if (mRestartLoader) { - mRestartLoader = false; - getLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this); - } - } - - @Override - protected void onPause() { - mIsPaused = true; - - super.onPause(); - } - - @Override - public void onBackPressed() { - // If in full screen mode, toggle mode & eat the 'back' - if (mFullScreen) { - toggleFullScreen(); - } else { - super.onBackPressed(); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putInt(STATE_ITEM_KEY, mViewPager.getCurrentItem()); - outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen); - } - - public void addScreenListener(OnScreenListener listener) { - mScreenListeners.add(listener); - } - - public void removeScreenListener(OnScreenListener listener) { - mScreenListeners.remove(listener); - } - - public synchronized void addCursorListener(CursorChangedListener listener) { - mCursorListeners.add(listener); - } - - public synchronized void removeCursorListener(CursorChangedListener listener) { - mCursorListeners.remove(listener); - } - - public boolean isFragmentFullScreen(Fragment fragment) { - if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) { - return mFullScreen; - } - return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment)); - } - - public void toggleFullScreen() { - setFullScreen(!mFullScreen, true); - } - - public void onPhotoRemoved(long photoId) { - final Cursor data = mAdapter.getCursor(); - if (data == null) { - // Huh?! How would this happen? - return; - } - - final int dataCount = data.getCount(); - if (dataCount <= 1) { - finish(); - return; - } - - getLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this); - } - - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - if (id == LOADER_PHOTO_LIST) { - return new PhotoPagerLoader(this, Uri.parse(mPhotosUri), mProjection); - } - return null; - } - - @Override - public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { - final int id = loader.getId(); - if (id == LOADER_PHOTO_LIST) { - if (data == null || data.getCount() == 0) { - mIsEmpty = true; - } else { - mAlbumCount = data.getCount(); - - // We're paused; don't do anything now, we'll get re-invoked - // when the activity becomes active again - // TODO(pwestbro): This shouldn't be necessary, as the loader manager should - // restart the loader - if (mIsPaused) { - mRestartLoader = true; - return; - } - mIsEmpty = false; - - // set the selected photo - int itemIndex = mPhotoIndex; - - // Use an index of 0 if the index wasn't specified or couldn't be found - if (itemIndex < 0) { - itemIndex = 0; - } - - mAdapter.swapCursor(data); - notifyCursorListeners(data); - - mViewPager.setCurrentItem(itemIndex, false); - setViewActivated(); - } - // Update the any action items - updateActionItems(); - } - } - - protected void updateActionItems() { - // Do nothing, but allow extending classes to do work - } - - private synchronized void notifyCursorListeners(Cursor data) { - // tell all of the objects listening for cursor changes - // that the cursor has changed - for (CursorChangedListener listener : mCursorListeners) { - listener.onCursorChanged(data); - } - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - // If the loader is reset, remove the reference in the adapter to this cursor - // TODO(pwestbro): reenable this when b/7075236 is fixed - // mAdapter.swapCursor(null); - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - @Override - public void onPageSelected(int position) { - mPhotoIndex = position; - setViewActivated(); - } - - @Override - public void onPageScrollStateChanged(int state) { - } - - public boolean isFragmentActive(Fragment fragment) { - if (mViewPager == null || mAdapter == null) { - return false; - } - return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment); - } - - public void onFragmentVisible(PhotoViewFragment fragment) { - updateActionBar(fragment); - } - - @Override - public InterceptType onTouchIntercept(float origX, float origY) { - boolean interceptLeft = false; - boolean interceptRight = false; - - for (OnScreenListener listener : mScreenListeners) { - if (!interceptLeft) { - interceptLeft = listener.onInterceptMoveLeft(origX, origY); - } - if (!interceptRight) { - interceptRight = listener.onInterceptMoveRight(origX, origY); - } - listener.onViewActivated(); - } - - if (interceptLeft) { - if (interceptRight) { - return InterceptType.BOTH; - } - return InterceptType.LEFT; - } else if (interceptRight) { - return InterceptType.RIGHT; - } - return InterceptType.NONE; - } - - /** - * Updates the title bar according to the value of {@link #mFullScreen}. - */ - private void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) { - final boolean fullScreenChanged = (fullScreen != mFullScreen); - mFullScreen = fullScreen; - - if (mFullScreen) { - setLightsOutMode(true); - cancelActionBarHideRunnable(); - } else { - setLightsOutMode(false); - if (setDelayedRunnable) { - postActionBarHideRunnableWithDelay(); - } - } - - if (fullScreenChanged) { - for (OnScreenListener listener : mScreenListeners) { - listener.onFullScreenChanged(mFullScreen); - } - } - } - - private void postActionBarHideRunnableWithDelay() { - mHandler.postDelayed(mActionBarHideRunnable, - mActionBarHideDelayTime); - } - - private void cancelActionBarHideRunnable() { - mHandler.removeCallbacks(mActionBarHideRunnable); - } - - private void setLightsOutMode(boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - int flags = enabled - ? View.SYSTEM_UI_FLAG_LOW_PROFILE - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - : View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; - - // using mViewPager since we have it and we need a view - mViewPager.setSystemUiVisibility(flags); - } else { - final ActionBar actionBar = getActionBar(); - if (enabled) { - actionBar.hide(); - } else { - actionBar.show(); - } - int flags = enabled - ? View.SYSTEM_UI_FLAG_LOW_PROFILE - : View.SYSTEM_UI_FLAG_VISIBLE; - mViewPager.setSystemUiVisibility(flags); - } - } - - private Runnable mActionBarHideRunnable = new Runnable() { - @Override - public void run() { - PhotoViewActivity.this.setLightsOutMode(true); - } - }; - - public void setViewActivated() { - for (OnScreenListener listener : mScreenListeners) { - listener.onViewActivated(); - } - } - - /** - * Adjusts the activity title and subtitle to reflect the photo name and count. - */ - protected void updateActionBar(PhotoViewFragment fragment) { - final int position = mViewPager.getCurrentItem() + 1; - final String title; - final String subtitle; - final boolean hasAlbumCount = mAlbumCount >= 0; - - final Cursor cursor = getCursorAtProperPosition(); - - if (cursor != null) { - final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME); - title = cursor.getString(photoNameIndex); - } else { - title = null; - } - - if (mIsEmpty || !hasAlbumCount || position <= 0) { - subtitle = null; - } else { - subtitle = getResources().getString(R.string.photo_view_count, position, mAlbumCount); - } - - final ActionBar actionBar = getActionBar(); - actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE); - actionBar.setTitle(title); - actionBar.setSubtitle(subtitle); - } - - /** - * Utility method that will return the cursor that contains the data - * at the current position so that it refers to the current image on screen. - * @return the cursor at the current position or - * null if no cursor exists or if the {@link PhotoViewPager} is null. - */ - public Cursor getCursorAtProperPosition() { - if (mViewPager == null) { - return null; - } - - final int position = mViewPager.getCurrentItem(); - final Cursor cursor = mAdapter.getCursor(); - - if (cursor == null) { - return null; - } - - cursor.moveToPosition(position); - - return cursor; - } - - public Cursor getCursor() { - return (mAdapter == null) ? null : mAdapter.getCursor(); - } - - @Override - public void onMenuVisibilityChanged(boolean isVisible) { - if (isVisible) { - cancelActionBarHideRunnable(); - } else { - postActionBarHideRunnableWithDelay(); - } - } - - public void onNewPhotoLoaded() { - } -} diff --git a/photoviewer/src/com/android/ex/photo/PhotoViewPager.java b/photoviewer/src/com/android/ex/photo/PhotoViewPager.java deleted file mode 100644 index 65d1d6d..0000000 --- a/photoviewer/src/com/android/ex/photo/PhotoViewPager.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo; - -import android.content.Context; -import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -/** - * View pager for photo view fragments. Define our own class so we can specify the - * view pager in XML. - */ -public class PhotoViewPager extends ViewPager { - /** - * A type of intercept that should be performed - */ - public static enum InterceptType { NONE, LEFT, RIGHT, BOTH } - - /** - * Provides an ability to intercept touch events. - * <p> - * {@link ViewPager} intercepts all touch events and we need to be able to override this - * behavior. Instead, we could perform a similar function by declaring a custom - * {@link ViewGroup} to contain the pager and intercept touch events at a higher level. - */ - public static interface OnInterceptTouchListener { - /** - * Called when a touch intercept is about to occur. - * - * @param origX the raw x coordinate of the initial touch - * @param origY the raw y coordinate of the initial touch - * @return Which type of touch, if any, should should be intercepted. - */ - public InterceptType onTouchIntercept(float origX, float origY); - } - - private static final int INVALID_POINTER = -1; - - private float mLastMotionX; - private int mActivePointerId; - /** The x coordinate where the touch originated */ - private float mActivatedX; - /** The y coordinate where the touch originated */ - private float mActivatedY; - private OnInterceptTouchListener mListener; - - public PhotoViewPager(Context context) { - super(context); - initialize(); - } - - public PhotoViewPager(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(); - } - - private void initialize() { - // Set the page transformer to perform the transition animation - // for each page in the view. - setPageTransformer(true, new PageTransformer() { - @Override - public void transformPage(View page, float position) { - - // The >= 1 is needed so that the page - // (page A) that transitions behind the newly visible - // page (page B) that comes in from the left does not - // get the touch events because it is still on screen - // (page A is still technically on screen despite being - // invisible). This makes sure that when the transition - // has completely finished, we revert it to its default - // behavior and move it off of the screen. - if (position < 0 || position >= 1.f) { - page.setTranslationX(0); - page.setAlpha(1.f); - page.setScaleX(1); - page.setScaleY(1); - } else { - page.setTranslationX(-position * page.getWidth()); - page.setAlpha(Math.max(0,1.f - position)); - final float scale = Math.max(0, 1.f - position * 0.3f); - page.setScaleX(scale); - page.setScaleY(scale); - } - } - }); - } - - /** - * {@inheritDoc} - * <p> - * We intercept touch event intercepts so we can prevent switching views when the - * current view is internally scrollable. - */ - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - final InterceptType intercept = (mListener != null) - ? mListener.onTouchIntercept(mActivatedX, mActivatedY) - : InterceptType.NONE; - final boolean ignoreScrollLeft = - (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT); - final boolean ignoreScrollRight = - (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT); - - // Only check ability to page if we can't scroll in one / both directions - final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; - - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - mActivePointerId = INVALID_POINTER; - } - - switch (action) { - case MotionEvent.ACTION_MOVE: { - if (ignoreScrollLeft || ignoreScrollRight) { - final int activePointerId = mActivePointerId; - if (activePointerId == INVALID_POINTER) { - // If we don't have a valid id, the touch down wasn't on content. - break; - } - - final int pointerIndex = - MotionEventCompat.findPointerIndex(ev, activePointerId); - final float x = MotionEventCompat.getX(ev, pointerIndex); - - if (ignoreScrollLeft && ignoreScrollRight) { - mLastMotionX = x; - return false; - } else if (ignoreScrollLeft && (x > mLastMotionX)) { - mLastMotionX = x; - return false; - } else if (ignoreScrollRight && (x < mLastMotionX)) { - mLastMotionX = x; - return false; - } - } - break; - } - - case MotionEvent.ACTION_DOWN: { - mLastMotionX = ev.getX(); - // Use the raw x/y as the children can be located anywhere and there isn't a - // single offset that would be meaningful - mActivatedX = ev.getRawX(); - mActivatedY = ev.getRawY(); - mActivePointerId = MotionEventCompat.getPointerId(ev, 0); - break; - } - - case MotionEventCompat.ACTION_POINTER_UP: { - final int pointerIndex = MotionEventCompat.getActionIndex(ev); - final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); - if (pointerId == mActivePointerId) { - // Our active pointer going up; select a new active pointer - final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex); - mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); - } - break; - } - } - - return super.onInterceptTouchEvent(ev); - } - - /** - * sets the intercept touch listener. - */ - public void setOnInterceptTouchListener(OnInterceptTouchListener l) { - mListener = l; - } -} diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java deleted file mode 100644 index 848d79a..0000000 --- a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.adapters; - -import android.app.Fragment; -import android.app.FragmentManager; -import android.content.Context; -import android.database.Cursor; -import android.util.Log; -import android.util.SparseIntArray; -import android.view.View; - -import com.android.ex.photo.provider.PhotoContract; - -import java.util.HashMap; - -/** - * Page adapter for use with an BaseCursorLoader. Unlike other cursor adapters, this has no - * observers for automatic refresh. Instead, it depends upon external mechanisms to provide - * the update signal. - */ -public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { - private static final String TAG = "BaseCursorPagerAdapter"; - - Context mContext; - private boolean mDataValid; - private Cursor mCursor; - private int mRowIDColumn; - /** Mapping of row ID to cursor position */ - private SparseIntArray mItemPosition; - /** Mapping of instantiated object to row ID */ - private HashMap<Object, Integer> mObjectRowMap = new HashMap<Object, Integer>(); - - /** - * Constructor that always enables auto-requery. - * - * @param c The cursor from which to get the data. - * @param context The context - */ - public BaseCursorPagerAdapter(Context context, FragmentManager fm, Cursor c) { - super(fm); - init(context, c); - } - - /** - * Makes a fragment for the data pointed to by the cursor - * - * @param context Interface to application's global information - * @param cursor The cursor from which to get the data. The cursor is already - * moved to the correct position. - * @return the newly created fragment. - */ - public abstract Fragment getItem(Context context, Cursor cursor, int position); - - @Override - public Fragment getItem(int position) { - if (mDataValid && moveCursorTo(position)) { - return getItem(mContext, mCursor, position); - } - return null; - } - - @Override - public int getCount() { - if (mDataValid && mCursor != null) { - return mCursor.getCount(); - } else { - return 0; - } - } - - @Override - public Object instantiateItem(View container, int position) { - if (!mDataValid) { - throw new IllegalStateException("this should only be called when the cursor is valid"); - } - - final Integer rowId; - if (moveCursorTo(position)) { - rowId = mCursor.getString(mRowIDColumn).hashCode(); - } else { - rowId = null; - } - - // Create the fragment and bind cursor data - final Object obj = super.instantiateItem(container, position); - if (obj != null) { - mObjectRowMap.put(obj, rowId); - } - return obj; - } - - @Override - public void destroyItem(View container, int position, Object object) { - mObjectRowMap.remove(object); - - super.destroyItem(container, position, object); - } - - @Override - public int getItemPosition(Object object) { - final Integer rowId = mObjectRowMap.get(object); - if (rowId == null || mItemPosition == null) { - return POSITION_NONE; - } - - final int position = mItemPosition.get(rowId, POSITION_NONE); - return position; - } - - /** - * @return true if data is valid - */ - public boolean isDataValid() { - return mDataValid; - } - - /** - * Returns the cursor. - */ - public Cursor getCursor() { - return mCursor; - } - - /** - * Returns the data item associated with the specified position in the data set. - */ - public Object getDataItem(int position) { - if (mDataValid && moveCursorTo(position)) { - return mCursor; - } else { - return null; - } - } - - /** - * Returns the row id associated with the specified position in the list. - */ - public long getItemId(int position) { - if (mDataValid && moveCursorTo(position)) { - return mCursor.getString(mRowIDColumn).hashCode(); - } else { - return 0; - } - } - - /** - * Swap in a new Cursor, returning the old Cursor. - * - * @param newCursor The new cursor to be used. - * @return Returns the previously set Cursor, or null if there was not one. - * If the given new Cursor is the same instance is the previously set - * Cursor, null is also returned. - */ - public Cursor swapCursor(Cursor newCursor) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "swapCursor old=" + (mCursor == null ? -1 : mCursor.getCount()) + - "; new=" + (newCursor == null ? -1 : newCursor.getCount())); - } - - if (newCursor == mCursor) { - return null; - } - Cursor oldCursor = mCursor; - mCursor = newCursor; - if (newCursor != null) { - mRowIDColumn = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI); - mDataValid = true; - } else { - mRowIDColumn = -1; - mDataValid = false; - } - - setItemPosition(); - notifyDataSetChanged(); // notify the observers about the new cursor - return oldCursor; - } - - /** - * Converts the cursor into a CharSequence. Subclasses should override this - * method to convert their results. The default implementation returns an - * empty String for null values or the default String representation of - * the value. - * - * @param cursor the cursor to convert to a CharSequence - * @return a CharSequence representing the value - */ - public CharSequence convertToString(Cursor cursor) { - return cursor == null ? "" : cursor.toString(); - } - - @Override - protected String makeFragmentName(int viewId, int index) { - if (moveCursorTo(index)) { - return "android:pager:" + viewId + ":" + mCursor.getString(mRowIDColumn).hashCode(); - } else { - return super.makeFragmentName(viewId, index); - } - } - - /** - * Moves the cursor to the given position - * - * @return {@code true} if the cursor's position was set. Otherwise, {@code false}. - */ - private boolean moveCursorTo(int position) { - if (mCursor != null && !mCursor.isClosed()) { - return mCursor.moveToPosition(position); - } - return false; - } - - /** - * Initialize the adapter. - */ - private void init(Context context, Cursor c) { - boolean cursorPresent = c != null; - mCursor = c; - mDataValid = cursorPresent; - mContext = context; - mRowIDColumn = cursorPresent - ? mCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI) : -1; - } - - /** - * Sets the {@link #mItemPosition} instance variable with the current mapping of - * row id to cursor position. - */ - private void setItemPosition() { - if (!mDataValid || mCursor == null || mCursor.isClosed()) { - mItemPosition = null; - return; - } - - SparseIntArray itemPosition = new SparseIntArray(mCursor.getCount()); - - mCursor.moveToPosition(-1); - while (mCursor.moveToNext()) { - final int rowId = mCursor.getString(mRowIDColumn).hashCode(); - final int position = mCursor.getPosition(); - - itemPosition.append(rowId, position); - } - mItemPosition = itemPosition; - } -} diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java deleted file mode 100644 index 9c24575..0000000 --- a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.adapters; - -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.os.Parcelable; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.PagerAdapter; -import android.util.Log; -import android.util.LruCache; -import android.view.View; - -/** - * NOTE: This is a direct copy of {@link FragmentPagerAdapter} with four very important - * modifications. - * <p> - * <ol> - * <li>The method {@link #makeFragmentName(int, int)} is declared "protected" - * in our class. We need to be able to re-define the fragment's name according to data - * only available to sub-classes.</li> - * <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search - * the entire view hierarchy for the given view.</li> - * <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and - * added to a cache. If the fragment is evicted from the cache, it will be deleted. - * An album may contain thousands of photos and we want to avoid having thousands of - * fragments.</li> - * </ol> - */ -public abstract class BaseFragmentPagerAdapter extends PagerAdapter { - /** The default size of {@link #mFragmentCache} */ - private static final int DEFAULT_CACHE_SIZE = 5; - private static final String TAG = "FragmentPagerAdapter"; - private static final boolean DEBUG = false; - - private final FragmentManager mFragmentManager; - private FragmentTransaction mCurTransaction = null; - private Fragment mCurrentPrimaryItem = null; - /** A cache to store detached fragments before they are removed */ - private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE); - - public BaseFragmentPagerAdapter(FragmentManager fm) { - mFragmentManager = fm; - } - - /** - * Return the Fragment associated with a specified position. - */ - public abstract Fragment getItem(int position); - - @Override - public void startUpdate(View container) { - } - - @Override - public Object instantiateItem(View container, int position) { - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction(); - } - - // Do we already have this fragment? - String name = makeFragmentName(container.getId(), position); - - // Remove item from the cache - mFragmentCache.remove(name); - - Fragment fragment = mFragmentManager.findFragmentByTag(name); - if (fragment != null) { - if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment); - mCurTransaction.attach(fragment); - } else { - fragment = getItem(position); - if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); - mCurTransaction.add(container.getId(), fragment, - makeFragmentName(container.getId(), position)); - } - if (fragment != mCurrentPrimaryItem) { - fragment.setMenuVisibility(false); - } - - return fragment; - } - - @Override - public void destroyItem(View container, int position, Object object) { - if (mCurTransaction == null) { - mCurTransaction = mFragmentManager.beginTransaction(); - } - if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object - + " v=" + ((Fragment)object).getView()); - - Fragment fragment = (Fragment) object; - String name = fragment.getTag(); - if (name == null) { - // We prefer to get the name directly from the fragment, but, if the fragment is - // detached before the add transaction is committed, this could be 'null'. In - // that case, generate a name so we can still cache the fragment. - name = makeFragmentName(container.getId(), position); - } - - mFragmentCache.put(name, fragment); - mCurTransaction.detach(fragment); - } - - @Override - public void setPrimaryItem(View container, int position, Object object) { - Fragment fragment = (Fragment) object; - if (fragment != mCurrentPrimaryItem) { - if (mCurrentPrimaryItem != null) { - mCurrentPrimaryItem.setMenuVisibility(false); - } - if (fragment != null) { - fragment.setMenuVisibility(true); - } - mCurrentPrimaryItem = fragment; - } - - } - - @Override - public void finishUpdate(View container) { - if (mCurTransaction != null) { - mCurTransaction.commitAllowingStateLoss(); - mCurTransaction = null; - mFragmentManager.executePendingTransactions(); - } - } - - @Override - public boolean isViewFromObject(View view, Object object) { - // Ascend the tree to determine if the view is a child of the fragment - View root = ((Fragment) object).getView(); - for (Object v = view; v instanceof View; v = ((View) v).getParent()) { - if (v == root) { - return true; - } - } - return false; - } - - @Override - public Parcelable saveState() { - return null; - } - - @Override - public void restoreState(Parcelable state, ClassLoader loader) { - } - - /** Creates a name for the fragment */ - protected String makeFragmentName(int viewId, int index) { - return "android:switcher:" + viewId + ":" + index; - } - - /** - * A cache of detached fragments. - */ - private class FragmentCache extends LruCache<String, Fragment> { - public FragmentCache(int size) { - super(size); - } - - @Override - protected void entryRemoved(boolean evicted, String key, - Fragment oldValue, Fragment newValue) { - // remove the fragment if it's evicted OR it's replaced by a new fragment - if (evicted || (newValue != null && oldValue != newValue)) { - mCurTransaction.remove(oldValue); - } - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java deleted file mode 100644 index bf75ecb..0000000 --- a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.adapters; - -import android.app.Fragment; -import android.app.FragmentManager; -import android.content.Context; -import android.database.Cursor; - -import com.android.ex.photo.Intents; -import com.android.ex.photo.Intents.PhotoViewIntentBuilder; -import com.android.ex.photo.fragments.PhotoViewFragment; -import com.android.ex.photo.provider.PhotoContract; - -/** - * Pager adapter for the photo view - */ -public class PhotoPagerAdapter extends BaseCursorPagerAdapter { - private int mContentUriIndex; - private int mThumbnailUriIndex; - - public PhotoPagerAdapter(Context context, FragmentManager fm, Cursor c) { - super(context, fm, c); - } - - @Override - public Fragment getItem(Context context, Cursor cursor, int position) { - final String photoUri = cursor.getString(mContentUriIndex); - final String thumbnailUri = cursor.getString(mThumbnailUriIndex); - - // create new PhotoViewFragment - final PhotoViewIntentBuilder builder = - Intents.newPhotoViewFragmentIntentBuilder(mContext); - builder - .setResolvedPhotoUri(photoUri) - .setThumbnailUri(thumbnailUri); - - return new PhotoViewFragment(builder.build(), position, this); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - if (newCursor != null) { - mContentUriIndex = - newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.CONTENT_URI); - mThumbnailUriIndex = - newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.THUMBNAIL_URI); - } else { - mContentUriIndex = -1; - mThumbnailUriIndex = -1; - } - - return super.swapCursor(newCursor); - } - - public String getPhotoUri(Cursor cursor) { - return cursor.getString(mContentUriIndex); - } -} diff --git a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java deleted file mode 100644 index efb8145..0000000 --- a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.fragments; - -import android.app.Activity; -import android.app.Fragment; -import android.app.LoaderManager; -import android.app.LoaderManager.LoaderCallbacks; -import android.content.Context; -import android.content.Intent; -import android.content.Loader; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.android.ex.photo.Intents; -import com.android.ex.photo.PhotoViewActivity; -import com.android.ex.photo.PhotoViewActivity.CursorChangedListener; -import com.android.ex.photo.PhotoViewActivity.OnScreenListener; -import com.android.ex.photo.R; -import com.android.ex.photo.adapters.PhotoPagerAdapter; -import com.android.ex.photo.loaders.PhotoBitmapLoader; -import com.android.ex.photo.util.ImageUtils; -import com.android.ex.photo.views.PhotoView; -import com.android.ex.photo.views.ProgressBarWrapper; - -/** - * Displays a photo. - */ -public class PhotoViewFragment extends Fragment implements - LoaderCallbacks<Bitmap>, OnClickListener, OnScreenListener, CursorChangedListener { - /** - * Interface for components that are internally scrollable left-to-right. - */ - public static interface HorizontallyScrollable { - /** - * Return {@code true} if the component needs to receive right-to-left - * touch movements. - * - * @param origX the raw x coordinate of the initial touch - * @param origY the raw y coordinate of the initial touch - */ - - public boolean interceptMoveLeft(float origX, float origY); - - /** - * Return {@code true} if the component needs to receive left-to-right - * touch movements. - * - * @param origX the raw x coordinate of the initial touch - * @param origY the raw y coordinate of the initial touch - */ - public boolean interceptMoveRight(float origX, float origY); - } - - private final static String STATE_INTENT_KEY = - "com.android.mail.photo.fragments.PhotoViewFragment.INTENT"; - - // Loader IDs - private final static int LOADER_ID_PHOTO = 1; - private final static int LOADER_ID_THUMBNAIL = 2; - - /** The size of the photo */ - public static Integer sPhotoSize; - - /** The URL of a photo to display */ - private String mResolvedPhotoUri; - private String mThumbnailUri; - /** The intent we were launched with */ - private Intent mIntent; - private PhotoViewActivity mCallback; - private PhotoPagerAdapter mAdapter; - - private PhotoView mPhotoView; - private ImageView mPhotoPreviewImage; - private TextView mEmptyText; - private ImageView mRetryButton; - private ProgressBarWrapper mPhotoProgressBar; - - private final int mPosition; - - /** Whether or not the fragment should make the photo full-screen */ - private boolean mFullScreen; - - /** Whether or not the progress bar is showing valid information about the progress stated */ - private boolean mProgressBarNeeded = true; - - private View mPhotoPreviewAndProgress; - - public PhotoViewFragment() { - mPosition = -1; - mProgressBarNeeded = true; - } - - public PhotoViewFragment(Intent intent, int position, PhotoPagerAdapter adapter) { - mIntent = intent; - mPosition = position; - mAdapter = adapter; - mProgressBarNeeded = true; - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mCallback = (PhotoViewActivity) activity; - if (mCallback == null) { - throw new IllegalArgumentException( - "Activity must be a derived class of PhotoViewActivity"); - } - - if (sPhotoSize == null) { - final DisplayMetrics metrics = new DisplayMetrics(); - final WindowManager wm = - (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE); - final ImageUtils.ImageSize imageSize = ImageUtils.sUseImageSize; - wm.getDefaultDisplay().getMetrics(metrics); - switch (imageSize) { - case EXTRA_SMALL: { - // Use a photo that's 80% of the "small" size - sPhotoSize = (Math.min(metrics.heightPixels, metrics.widthPixels) * 800) / 1000; - break; - } - - case SMALL: - case NORMAL: - default: { - sPhotoSize = Math.min(metrics.heightPixels, metrics.widthPixels); - break; - } - } - } - } - - @Override - public void onDetach() { - mCallback = null; - super.onDetach(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (savedInstanceState != null) { - final Bundle state = savedInstanceState.getBundle(STATE_INTENT_KEY); - if (state != null) { - mIntent = new Intent().putExtras(state); - } - } - - if (mIntent != null) { - mResolvedPhotoUri = mIntent.getStringExtra(Intents.EXTRA_RESOLVED_PHOTO_URI); - mThumbnailUri = mIntent.getStringExtra(Intents.EXTRA_THUMBNAIL_URI); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.photo_fragment_view, container, false); - - mPhotoView = (PhotoView) view.findViewById(R.id.photo_view); - mPhotoView.setOnClickListener(this); - mPhotoView.setFullScreen(mFullScreen, false); - mPhotoView.enableImageTransforms(true); - - mPhotoPreviewAndProgress = view.findViewById(R.id.photo_preview); - mPhotoPreviewImage = (ImageView) view.findViewById(R.id.photo_preview_image); - final ProgressBar indeterminate = - (ProgressBar) view.findViewById(R.id.indeterminate_progress); - final ProgressBar determinate = - (ProgressBar) view.findViewById(R.id.determinate_progress); - mPhotoProgressBar = new ProgressBarWrapper(determinate, indeterminate, true); - mEmptyText = (TextView) view.findViewById(R.id.empty_text); - mRetryButton = (ImageView) view.findViewById(R.id.retry_button); - - // Don't call until we've setup the entire view - setViewVisibility(); - - return view; - } - - @Override - public void onResume() { - mCallback.addScreenListener(this); - mCallback.addCursorListener(this); - - getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this); - - super.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - // Remove listeners - mCallback.removeCursorListener(this); - mCallback.removeScreenListener(this); - resetPhotoView(); - } - - @Override - public void onDestroyView() { - // Clean up views and other components - if (mPhotoView != null) { - mPhotoView.clear(); - mPhotoView = null; - } - - super.onDestroyView(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - if (mIntent != null) { - outState.putParcelable(STATE_INTENT_KEY, mIntent.getExtras()); - } - } - - @Override - public Loader<Bitmap> onCreateLoader(int id, Bundle args) { - switch (id) { - case LOADER_ID_PHOTO: - return new PhotoBitmapLoader(getActivity(), mResolvedPhotoUri); - case LOADER_ID_THUMBNAIL: - return new PhotoBitmapLoader(getActivity(), mThumbnailUri); - default: - return null; - } - } - - @Override - public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) { - // If we don't have a view, the fragment has been paused. We'll get the cursor again later. - if (getView() == null) { - return; - } - - final int id = loader.getId(); - switch (id) { - case LOADER_ID_PHOTO: - if (data != null) { - bindPhoto(data); - mPhotoPreviewAndProgress.setVisibility(View.GONE); - mProgressBarNeeded = false; - } - break; - case LOADER_ID_THUMBNAIL: - if (isPhotoBound()) { - // There is need to do anything with the thumbnail image, as the full size - // image is being shown. - mPhotoPreviewAndProgress.setVisibility(View.GONE); - mProgressBarNeeded = false; - return; - } else { - // Make the preview image view visible - mPhotoPreviewImage.setVisibility(View.VISIBLE); - - if (data == null) { - // no preview, show default - mPhotoPreviewImage.setImageResource(R.drawable.default_image); - mPhotoPreviewImage.setScaleType(ImageView.ScaleType.CENTER); - } else { - // Show the preview - mPhotoPreviewImage.setImageBitmap(data); - } - // Now load the full size image - getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this); - } - break; - default: - break; - } - - if (mProgressBarNeeded == false) { - // Hide the progress bar as it isn't needed anymore. - mPhotoProgressBar.setVisibility(View.GONE); - } - if (data != null) { - mCallback.onNewPhotoLoaded(); - } - - setViewVisibility(); - } - - /** - * Binds an image to the photo view. - */ - private void bindPhoto(Bitmap bitmap) { - if (mPhotoView != null) { - mPhotoView.bindPhoto(bitmap); - } - } - - /** - * Resets the photo view to it's default state w/ no bound photo. - */ - private void resetPhotoView() { - if (mPhotoView != null) { - mPhotoView.bindPhoto(null); - } - } - - @Override - public void onLoaderReset(Loader<Bitmap> loader) { - // Do nothing - } - - @Override - public void onClick(View v) { - mCallback.toggleFullScreen(); - } - - @Override - public void onFullScreenChanged(boolean fullScreen) { - setViewVisibility(); - } - - @Override - public void onViewActivated() { - if (!mCallback.isFragmentActive(this)) { - // we're not in the foreground; reset our view - resetViews(); - } else { - if (!isPhotoBound()) { - // Restart the loader - getLoaderManager().restartLoader(LOADER_ID_THUMBNAIL, null, this); - } - mCallback.onFragmentVisible(this); - } - } - - /** - * Reset the views to their default states - */ - public void resetViews() { - if (mPhotoView != null) { - mPhotoView.resetTransformations(); - } - } - - @Override - public boolean onInterceptMoveLeft(float origX, float origY) { - if (!mCallback.isFragmentActive(this)) { - // we're not in the foreground; don't intercept any touches - return false; - } - - return (mPhotoView != null && mPhotoView.interceptMoveLeft(origX, origY)); - } - - @Override - public boolean onInterceptMoveRight(float origX, float origY) { - if (!mCallback.isFragmentActive(this)) { - // we're not in the foreground; don't intercept any touches - return false; - } - - return (mPhotoView != null && mPhotoView.interceptMoveRight(origX, origY)); - } - - /** - * Returns {@code true} if a photo has been bound. Otherwise, returns {@code false}. - */ - public boolean isPhotoBound() { - return (mPhotoView != null && mPhotoView.isPhotoBound()); - } - - /** - * Sets view visibility depending upon whether or not we're in "full screen" mode. - */ - private void setViewVisibility() { - final boolean fullScreen = mCallback.isFragmentFullScreen(this); - final boolean hide = fullScreen; - - setFullScreen(hide); - } - - /** - * Sets full-screen mode for the views. - */ - public void setFullScreen(boolean fullScreen) { - mFullScreen = fullScreen; - } - - @Override - public void onCursorChanged(Cursor cursor) { - if (cursor.moveToPosition(mPosition) && !isPhotoBound()) { - final LoaderManager manager = getLoaderManager(); - final Loader<Bitmap> fakeLoader = manager.getLoader(LOADER_ID_PHOTO); - if (fakeLoader == null) { - return; - } - - final PhotoBitmapLoader loader = - (PhotoBitmapLoader) fakeLoader; - mResolvedPhotoUri = mAdapter.getPhotoUri(cursor); - loader.setPhotoUri(mResolvedPhotoUri); - loader.forceLoad(); - } - } - - public ProgressBarWrapper getPhotoProgressBar() { - return mPhotoProgressBar; - } - - public TextView getEmptyText() { - return mEmptyText; - } - - public ImageView getRetryButton() { - return mRetryButton; - } - - public boolean isProgressBarNeeded() { - return mProgressBarNeeded; - } -} diff --git a/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java b/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java deleted file mode 100644 index fe42e0d..0000000 --- a/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.loaders; - -import android.content.AsyncTaskLoader; -import android.content.ContentResolver; -import android.content.Context; -import android.graphics.Bitmap; -import android.net.Uri; -import android.util.DisplayMetrics; - -import com.android.ex.photo.fragments.PhotoViewFragment; -import com.android.ex.photo.util.ImageUtils; - -/** - * Loader for the bitmap of a photo. - */ -public class PhotoBitmapLoader extends AsyncTaskLoader<Bitmap> { - private String mPhotoUri; - - private Bitmap mBitmap; - - public PhotoBitmapLoader(Context context, String photoUri) { - super(context); - mPhotoUri = photoUri; - } - - public void setPhotoUri(String photoUri) { - mPhotoUri = photoUri; - } - - @Override - public Bitmap loadInBackground() { - Context context = getContext(); - - if (context != null && mPhotoUri != null) { - final ContentResolver resolver = context.getContentResolver(); - Bitmap bitmap = ImageUtils.createLocalBitmap(resolver, Uri.parse(mPhotoUri), - PhotoViewFragment.sPhotoSize); - if (bitmap != null) { - bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM); - } - return bitmap; - } - - return null; - } - - /** - * Called when there is new data to deliver to the client. The - * super class will take care of delivering it; the implementation - * here just adds a little more logic. - */ - @Override - public void deliverResult(Bitmap bitmap) { - if (isReset()) { - // An async query came in while the loader is stopped. We - // don't need the result. - if (bitmap != null) { - onReleaseResources(bitmap); - } - } - Bitmap oldBitmap = mBitmap; - mBitmap = bitmap; - - if (isStarted()) { - // If the Loader is currently started, we can immediately - // deliver its results. - super.deliverResult(bitmap); - } - - // At this point we can release the resources associated with - // 'oldBitmap' if needed; now that the new result is delivered we - // know that it is no longer in use. - if (oldBitmap != null && oldBitmap != bitmap && !oldBitmap.isRecycled()) { - onReleaseResources(oldBitmap); - } - } - - /** - * Handles a request to start the Loader. - */ - @Override - protected void onStartLoading() { - if (mBitmap != null) { - // If we currently have a result available, deliver it - // immediately. - deliverResult(mBitmap); - } - - if (takeContentChanged() || mBitmap == null) { - // If the data has changed since the last time it was loaded - // or is not currently available, start a load. - forceLoad(); - } - } - - /** - * Handles a request to stop the Loader. - */ - @Override protected void onStopLoading() { - // Attempt to cancel the current load task if possible. - cancelLoad(); - } - - /** - * Handles a request to cancel a load. - */ - @Override - public void onCanceled(Bitmap bitmap) { - super.onCanceled(bitmap); - - // At this point we can release the resources associated with 'bitmap' - // if needed. - onReleaseResources(bitmap); - } - - /** - * Handles a request to completely reset the Loader. - */ - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped - onStopLoading(); - - // At this point we can release the resources associated with 'bitmap' - // if needed. - if (mBitmap != null) { - onReleaseResources(mBitmap); - mBitmap = null; - } - } - - /** - * Helper function to take care of releasing resources associated - * with an actively loaded data set. - */ - protected void onReleaseResources(Bitmap bitmap) { - if (bitmap != null && !bitmap.isRecycled()) { - bitmap.recycle(); - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java b/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java deleted file mode 100644 index 7ba932b..0000000 --- a/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.loaders; - -import android.content.Context; -import android.content.CursorLoader; -import android.database.Cursor; -import android.net.Uri; - -import com.android.ex.photo.provider.PhotoContract; - -/** - * Loader for a set of photo IDs. - */ -public class PhotoPagerLoader extends CursorLoader { - private final Uri mPhotosUri; - private final String[] mProjection; - - public PhotoPagerLoader( - Context context, Uri photosUri, String[] projection) { - super(context); - mPhotosUri = photosUri; - mProjection = projection != null ? projection : PhotoContract.PhotoQuery.PROJECTION; - } - - @Override - public Cursor loadInBackground() { - Cursor returnCursor = null; - - final Uri loaderUri = mPhotosUri.buildUpon().appendQueryParameter( - PhotoContract.ContentTypeParameters.CONTENT_TYPE, "image/").build(); - setUri(loaderUri); - setProjection(mProjection); - returnCursor = super.loadInBackground(); - - return returnCursor; - } -} diff --git a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java b/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java deleted file mode 100644 index 439b68b..0000000 --- a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.provider; - -import android.net.Uri; -import android.provider.OpenableColumns; - -public final class PhotoContract { - /** Columns for the view */ - public static interface PhotoViewColumns { - /** - * This column is a {@link Uri} that can be queried - * for this individual image (resulting cursor has one single row for this image). - */ - public static final String URI = "uri"; - /** - * This column is a {@link String} that can be queried for this - * individual image to return a displayable name. - */ - public static final String NAME = OpenableColumns.DISPLAY_NAME; - /** - * This column is a {@link Uri} that points to the downloaded local file. - * Can be null. - */ - public static final String CONTENT_URI = "contentUri"; - /** - * This column is a {@link Uri} that points to a thumbnail of the image - * that ideally is a local file. - * Can be null. - */ - public static final String THUMBNAIL_URI = "thumbnailUri"; - /** - * This string column is the MIME type. - */ - public static final String CONTENT_TYPE = "contentType"; - - } - - public static interface PhotoQuery { - /** Projection of the returned cursor */ - public final static String[] PROJECTION = { - PhotoViewColumns.URI, - PhotoViewColumns.NAME, - PhotoViewColumns.CONTENT_URI, - PhotoViewColumns.THUMBNAIL_URI, - PhotoViewColumns.CONTENT_TYPE, - }; - } - - public static final class ContentTypeParameters { - /** - * Parameter used to specify which type of content to return. - * Allows multiple types to be specified. - */ - public static final String CONTENT_TYPE = "contentType"; - - private ContentTypeParameters() {} - } -} diff --git a/photoviewer/src/com/android/ex/photo/util/Exif.java b/photoviewer/src/com/android/ex/photo/util/Exif.java deleted file mode 100644 index 743b896..0000000 --- a/photoviewer/src/com/android/ex/photo/util/Exif.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.util; - -import android.util.Log; - -public class Exif { - private static final String TAG = "CameraExif"; - - // Returns the degrees in clockwise. Values are 0, 90, 180, or 270. - public static int getOrientation(byte[] jpeg) { - if (jpeg == null) { - return 0; - } - - int offset = 0; - int length = 0; - - // ISO/IEC 10918-1:1993(E) - while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) { - int marker = jpeg[offset] & 0xFF; - - // Check if the marker is a padding. - if (marker == 0xFF) { - continue; - } - offset++; - - // Check if the marker is SOI or TEM. - if (marker == 0xD8 || marker == 0x01) { - continue; - } - // Check if the marker is EOI or SOS. - if (marker == 0xD9 || marker == 0xDA) { - break; - } - - // Get the length and check if it is reasonable. - length = pack(jpeg, offset, 2, false); - if (length < 2 || offset + length > jpeg.length) { - Log.e(TAG, "Invalid length"); - return 0; - } - - // Break if the marker is EXIF in APP1. - if (marker == 0xE1 && length >= 8 && - pack(jpeg, offset + 2, 4, false) == 0x45786966 && - pack(jpeg, offset + 6, 2, false) == 0) { - offset += 8; - length -= 8; - break; - } - - // Skip other markers. - offset += length; - length = 0; - } - - // JEITA CP-3451 Exif Version 2.2 - if (length > 8) { - // Identify the byte order. - int tag = pack(jpeg, offset, 4, false); - if (tag != 0x49492A00 && tag != 0x4D4D002A) { - Log.e(TAG, "Invalid byte order"); - return 0; - } - boolean littleEndian = (tag == 0x49492A00); - - // Get the offset and check if it is reasonable. - int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; - if (count < 10 || count > length) { - Log.e(TAG, "Invalid offset"); - return 0; - } - offset += count; - length -= count; - - // Get the count and go through all the elements. - count = pack(jpeg, offset - 2, 2, littleEndian); - while (count-- > 0 && length >= 12) { - // Get the tag and check if it is orientation. - tag = pack(jpeg, offset, 2, littleEndian); - if (tag == 0x0112) { - // We do not really care about type and count, do we? - int orientation = pack(jpeg, offset + 8, 2, littleEndian); - switch (orientation) { - case 1: - return 0; - case 3: - return 180; - case 6: - return 90; - case 8: - return 270; - } - Log.i(TAG, "Unsupported orientation"); - return 0; - } - offset += 12; - length -= 12; - } - } - - Log.i(TAG, "Orientation not found"); - return 0; - } - - private static int pack(byte[] bytes, int offset, int length, - boolean littleEndian) { - int step = 1; - if (littleEndian) { - offset += length - 1; - step = -1; - } - - int value = 0; - while (length-- > 0) { - value = (value << 8) | (bytes[offset] & 0xFF); - offset += step; - } - return value; - } -} diff --git a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java deleted file mode 100644 index 7fe971a..0000000 --- a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.util; - -import android.content.ContentResolver; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.Rect; -import android.net.Uri; -import android.os.Build; -import android.util.DisplayMetrics; -import android.util.Log; - -import com.android.ex.photo.PhotoViewActivity; -import com.android.ex.photo.util.Exif; - -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -/** - * Image utilities - */ -public class ImageUtils { - // Logging - private static final String TAG = "ImageUtils"; - - /** Minimum class memory class to use full-res photos */ - private final static long MIN_NORMAL_CLASS = 32; - /** Minimum class memory class to use small photos */ - private final static long MIN_SMALL_CLASS = 24; - - public static enum ImageSize { - EXTRA_SMALL, - SMALL, - NORMAL, - } - - public static final ImageSize sUseImageSize; - static { - // On HC and beyond, assume devices are more capable - if (Build.VERSION.SDK_INT >= 11) { - sUseImageSize = ImageSize.NORMAL; - } else { - if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) { - // We have plenty of memory; use full sized photos - sUseImageSize = ImageSize.NORMAL; - } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) { - // We have slight less memory; use smaller sized photos - sUseImageSize = ImageSize.SMALL; - } else { - // We have little memory; use very small sized photos - sUseImageSize = ImageSize.EXTRA_SMALL; - } - } - } - - /** - * @return true if the MimeType type is image - */ - public static boolean isImageMimeType(String mimeType) { - return mimeType != null && mimeType.startsWith("image/"); - } - - /** - * Create a bitmap from a local URI - * - * @param resolver The ContentResolver - * @param uri The local URI - * @param maxSize The maximum size (either width or height) - * - * @return The new bitmap or null - */ - public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) { - InputStream inputStream = null; - try { - final BitmapFactory.Options opts = new BitmapFactory.Options(); - final Point bounds = getImageBounds(resolver, uri); - - inputStream = resolver.openInputStream(uri); - opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize); - - final Bitmap decodedBitmap = decodeStream(inputStream, null, opts); - - // Correct thumbnail orientation as necessary - // TODO: Fix rotation if it's actually a problem - //return rotateBitmap(resolver, uri, decodedBitmap); - return decodedBitmap; - - } catch (FileNotFoundException exception) { - // Do nothing - the photo will appear to be missing - } catch (IOException exception) { - // Do nothing - the photo will appear to be missing - } catch (IllegalArgumentException exception) { - // Do nothing - the photo will appear to be missing - } finally { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (IOException ignore) { - } - } - return null; - } - - /** - * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect, - * BitmapFactory.Options)} that returns {@code null} on {@link - * OutOfMemoryError}. - * - * @param is The input stream that holds the raw data to be decoded into a - * bitmap. - * @param outPadding If not null, return the padding rect for the bitmap if - * it exists, otherwise set padding to [-1,-1,-1,-1]. If - * no bitmap is returned (null) then padding is - * unchanged. - * @param opts null-ok; Options that control downsampling and whether the - * image should be completely decoded, or just is size returned. - * @return The decoded bitmap, or null if the image data could not be - * decoded, or, if opts is non-null, if opts requested only the - * size be returned (in opts.outWidth and opts.outHeight) - */ - public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) { - ByteArrayOutputStream out = null; - try { - out = new ByteArrayOutputStream(); - final byte[] buffer = new byte[4096]; - int n = is.read(buffer); - while (n >= 0) { - out.write(buffer, 0, n); - n = is.read(buffer); - } - final byte[] bitmapBytes = out.toByteArray(); - - // Determine the orientation for this image - final int orientation = Exif.getOrientation(bitmapBytes); - final Bitmap originalBitmap = - BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, opts); - - if (originalBitmap != null && orientation != 0) { - final Matrix matrix = new Matrix(); - matrix.postRotate(orientation); - return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(), - originalBitmap.getHeight(), matrix, true); - } - return originalBitmap; - } catch (OutOfMemoryError oome) { - Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome); - return null; - } catch (IOException ioe) { - Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe); - return null; - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - // Do nothing - } - } - } - } - - /** - * Gets the image bounds - * - * @param resolver The ContentResolver - * @param uri The uri - * - * @return The image bounds - */ - private static Point getImageBounds(ContentResolver resolver, Uri uri) - throws IOException { - final BitmapFactory.Options opts = new BitmapFactory.Options(); - InputStream inputStream = null; - - try { - opts.inJustDecodeBounds = true; - inputStream = resolver.openInputStream(uri); - decodeStream(inputStream, null, opts); - - return new Point(opts.outWidth, opts.outHeight); - } finally { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (IOException ignore) { - } - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/views/PhotoView.java b/photoviewer/src/com/android/ex/photo/views/PhotoView.java deleted file mode 100644 index 8575bb1..0000000 --- a/photoviewer/src/com/android/ex/photo/views/PhotoView.java +++ /dev/null @@ -1,1288 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.views; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.support.v4.view.GestureDetectorCompat; -import android.util.AttributeSet; -import android.view.GestureDetector.OnGestureListener; -import android.view.GestureDetector.OnDoubleTapListener; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.View; - -import com.android.ex.photo.R; -import com.android.ex.photo.fragments.PhotoViewFragment.HorizontallyScrollable; - -/** - * Layout for the photo list view header. - */ -public class PhotoView extends View implements OnGestureListener, - OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener, - HorizontallyScrollable { - /** Zoom animation duration; in milliseconds */ - private final static long ZOOM_ANIMATION_DURATION = 300L; - /** Rotate animation duration; in milliseconds */ - private final static long ROTATE_ANIMATION_DURATION = 500L; - /** Snap animation duration; in milliseconds */ - private static final long SNAP_DURATION = 100L; - /** Amount of time to wait before starting snap animation; in milliseconds */ - private static final long SNAP_DELAY = 250L; - /** By how much to scale the image when double click occurs */ - private final static float DOUBLE_TAP_SCALE_FACTOR = 1.5f; - /** Amount of translation needed before starting a snap animation */ - private final static float SNAP_THRESHOLD = 20.0f; - /** The width & height of the bitmap returned by {@link #getCroppedPhoto()} */ - private final static float CROPPED_SIZE = 256.0f; - - /** If {@code true}, the static values have been initialized */ - private static boolean sInitialized; - - // Various dimensions - /** Width & height of the crop region */ - private static int sCropSize; - - // Bitmaps - /** Video icon */ - private static Bitmap sVideoImage; - /** Video icon */ - private static Bitmap sVideoNotReadyImage; - - // Features - private static boolean sHasMultitouchDistinct; - - // Paints - /** Paint to partially dim the photo during crop */ - private static Paint sCropDimPaint; - /** Paint to highlight the cropped portion of the photo */ - private static Paint sCropPaint; - - /** The photo to display */ - private BitmapDrawable mDrawable; - /** The matrix used for drawing; this may be {@code null} */ - private Matrix mDrawMatrix; - /** A matrix to apply the scaling of the photo */ - private Matrix mMatrix = new Matrix(); - /** The original matrix for this image; used to reset any transformations applied by the user */ - private Matrix mOriginalMatrix = new Matrix(); - - /** The fixed height of this view. If {@code -1}, calculate the height */ - private int mFixedHeight = -1; - /** When {@code true}, the view has been laid out */ - private boolean mHaveLayout; - /** Whether or not the photo is full-screen */ - private boolean mFullScreen; - /** Whether or not this is a still image of a video */ - private byte[] mVideoBlob; - /** Whether or not this is a still image of a video */ - private boolean mVideoReady; - - /** Whether or not crop is allowed */ - private boolean mAllowCrop; - /** The crop region */ - private Rect mCropRect = new Rect(); - /** Actual crop size; may differ from {@link #sCropSize} if the screen is smaller */ - private int mCropSize; - - /** Gesture detector */ - private GestureDetectorCompat mGestureDetector; - /** Gesture detector that detects pinch gestures */ - private ScaleGestureDetector mScaleGetureDetector; - /** An external click listener */ - private OnClickListener mExternalClickListener; - /** When {@code true}, allows gestures to scale / pan the image */ - private boolean mTransformsEnabled; - - // To support zooming - /** When {@code true}, a double tap scales the image by {@link #DOUBLE_TAP_SCALE_FACTOR} */ - private boolean mDoubleTapToZoomEnabled = true; - /** When {@code true}, prevents scale end gesture from falsely triggering a double click. */ - private boolean mDoubleTapDebounce; - /** When {@code false}, event is a scale gesture. Otherwise, event is a double touch. */ - private boolean mIsDoubleTouch; - /** Runnable that scales the image */ - private ScaleRunnable mScaleRunnable; - /** Minimum scale the image can have. */ - private float mMinScale; - /** Maximum scale to limit scaling to, 0 means no limit. */ - private float mMaxScale; - - // To support translation [i.e. panning] - /** Runnable that can move the image */ - private TranslateRunnable mTranslateRunnable; - private SnapRunnable mSnapRunnable; - - // To support rotation - /** The rotate runnable used to animate rotations of the image */ - private RotateRunnable mRotateRunnable; - /** The current rotation amount, in degrees */ - private float mRotation; - - // Convenience fields - // These are declared here not because they are important properties of the view. Rather, we - // declare them here to avoid object allocation during critical graphics operations; such as - // layout or drawing. - /** Source (i.e. the photo size) bounds */ - private RectF mTempSrc = new RectF(); - /** Destination (i.e. the display) bounds. The image is scaled to this size. */ - private RectF mTempDst = new RectF(); - /** Rectangle to handle translations */ - private RectF mTranslateRect = new RectF(); - /** Array to store a copy of the matrix values */ - private float[] mValues = new float[9]; - - public PhotoView(Context context) { - super(context); - initialize(); - } - - public PhotoView(Context context, AttributeSet attrs) { - super(context, attrs); - initialize(); - } - - public PhotoView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initialize(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (mScaleGetureDetector == null || mGestureDetector == null) { - // We're being destroyed; ignore any touch events - return true; - } - - mScaleGetureDetector.onTouchEvent(event); - mGestureDetector.onTouchEvent(event); - final int action = event.getAction(); - - switch (action) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (!mTranslateRunnable.mRunning) { - snap(); - } - break; - } - - return true; - } - - @Override - public boolean onDoubleTap(MotionEvent e) { - if (mDoubleTapToZoomEnabled && mTransformsEnabled) { - if (!mDoubleTapDebounce) { - float currentScale = getScale(); - float targetScale = currentScale * DOUBLE_TAP_SCALE_FACTOR; - - // Ensure the target scale is within our bounds - targetScale = Math.max(mMinScale, targetScale); - targetScale = Math.min(mMaxScale, targetScale); - - mScaleRunnable.start(currentScale, targetScale, e.getX(), e.getY()); - } - mDoubleTapDebounce = false; - } - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - return true; - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent e) { - if (mExternalClickListener != null && !mIsDoubleTouch) { - mExternalClickListener.onClick(this); - } - mIsDoubleTouch = false; - return true; - } - - @Override - public boolean onSingleTapUp(MotionEvent e) { - return false; - } - - @Override - public void onLongPress(MotionEvent e) { - } - - @Override - public void onShowPress(MotionEvent e) { - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (mTransformsEnabled) { - translate(-distanceX, -distanceY); - } - return true; - } - - @Override - public boolean onDown(MotionEvent e) { - if (mTransformsEnabled) { - mTranslateRunnable.stop(); - mSnapRunnable.stop(); - } - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (mTransformsEnabled) { - mTranslateRunnable.start(velocityX, velocityY); - } - return true; - } - - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (mTransformsEnabled) { - mIsDoubleTouch = false; - float currentScale = getScale(); - float newScale = currentScale * detector.getScaleFactor(); - scale(newScale, detector.getFocusX(), detector.getFocusY()); - } - return true; - } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - if (mTransformsEnabled) { - mScaleRunnable.stop(); - mIsDoubleTouch = true; - } - return true; - } - - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - if (mTransformsEnabled && mIsDoubleTouch) { - mDoubleTapDebounce = true; - resetTransformations(); - } - } - - @Override - public void setOnClickListener(OnClickListener listener) { - mExternalClickListener = listener; - } - - @Override - public boolean interceptMoveLeft(float origX, float origY) { - if (!mTransformsEnabled) { - // Allow intercept if we're not in transform mode - return false; - } else if (mTranslateRunnable.mRunning) { - // Don't allow touch intercept until we've stopped flinging - return true; - } else { - mMatrix.getValues(mValues); - mTranslateRect.set(mTempSrc); - mMatrix.mapRect(mTranslateRect); - - final float viewWidth = getWidth(); - final float transX = mValues[Matrix.MTRANS_X]; - final float drawWidth = mTranslateRect.right - mTranslateRect.left; - - if (!mTransformsEnabled || drawWidth <= viewWidth) { - // Allow intercept if not in transform mode or the image is smaller than the view - return false; - } else if (transX == 0) { - // We're at the left-side of the image; allow intercepting movements to the right - return false; - } else if (viewWidth >= drawWidth + transX) { - // We're at the right-side of the image; allow intercepting movements to the left - return true; - } else { - // We're in the middle of the image; don't allow touch intercept - return true; - } - } - } - - @Override - public boolean interceptMoveRight(float origX, float origY) { - if (!mTransformsEnabled) { - // Allow intercept if we're not in transform mode - return false; - } else if (mTranslateRunnable.mRunning) { - // Don't allow touch intercept until we've stopped flinging - return true; - } else { - mMatrix.getValues(mValues); - mTranslateRect.set(mTempSrc); - mMatrix.mapRect(mTranslateRect); - - final float viewWidth = getWidth(); - final float transX = mValues[Matrix.MTRANS_X]; - final float drawWidth = mTranslateRect.right - mTranslateRect.left; - - if (!mTransformsEnabled || drawWidth <= viewWidth) { - // Allow intercept if not in transform mode or the image is smaller than the view - return false; - } else if (transX == 0) { - // We're at the left-side of the image; allow intercepting movements to the right - return true; - } else if (viewWidth >= drawWidth + transX) { - // We're at the right-side of the image; allow intercepting movements to the left - return false; - } else { - // We're in the middle of the image; don't allow touch intercept - return true; - } - } - } - - /** - * Free all resources held by this view. - * The view is on its way to be collected and will not be reused. - */ - public void clear() { - mGestureDetector = null; - mScaleGetureDetector = null; - mDrawable = null; - mScaleRunnable.stop(); - mScaleRunnable = null; - mTranslateRunnable.stop(); - mTranslateRunnable = null; - mSnapRunnable.stop(); - mSnapRunnable = null; - mRotateRunnable.stop(); - mRotateRunnable = null; - setOnClickListener(null); - mExternalClickListener = null; - } - - /** - * Binds a bitmap to the view. - * - * @param photoBitmap the bitmap to bind. - */ - public void bindPhoto(Bitmap photoBitmap) { - boolean changed = false; - if (mDrawable != null) { - final Bitmap drawableBitmap = mDrawable.getBitmap(); - if (photoBitmap == drawableBitmap) { - // setting the same bitmap; do nothing - return; - } - - changed = photoBitmap != null && - (mDrawable.getIntrinsicWidth() != photoBitmap.getWidth() || - mDrawable.getIntrinsicHeight() != photoBitmap.getHeight()); - - // Reset mMinScale to ensure the bounds / matrix are recalculated - mMinScale = 0f; - mDrawable = null; - } - - if (mDrawable == null && photoBitmap != null) { - mDrawable = new BitmapDrawable(getResources(), photoBitmap); - } - - configureBounds(changed); - invalidate(); - } - - /** - * Returns the bound photo data if set. Otherwise, {@code null}. - */ - public Bitmap getPhoto() { - if (mDrawable != null) { - return mDrawable.getBitmap(); - } - return null; - } - - /** - * Gets video data associated with this item. Returns {@code null} if this is not a video. - */ - public byte[] getVideoData() { - return mVideoBlob; - } - - /** - * Returns {@code true} if the photo represents a video. Otherwise, {@code false}. - */ - public boolean isVideo() { - return mVideoBlob != null; - } - - /** - * Returns {@code true} if the video is ready to play. Otherwise, {@code false}. - */ - public boolean isVideoReady() { - return mVideoBlob != null && mVideoReady; - } - - /** - * Returns {@code true} if a photo has been bound. Otherwise, {@code false}. - */ - public boolean isPhotoBound() { - return mDrawable != null; - } - - /** - * Hides the photo info portion of the header. As a side effect, this automatically enables - * or disables image transformations [eg zoom, pan, etc...] depending upon the value of - * fullScreen. If this is not desirable, enable / disable image transformations manually. - */ - public void setFullScreen(boolean fullScreen, boolean animate) { - if (fullScreen != mFullScreen) { - mFullScreen = fullScreen; - requestLayout(); - invalidate(); - } - } - - /** - * Enable or disable cropping of the displayed image. Cropping can only be enabled - * <em>before</em> the view has been laid out. Additionally, once cropping has been - * enabled, it cannot be disabled. - */ - public void enableAllowCrop(boolean allowCrop) { - if (allowCrop && mHaveLayout) { - throw new IllegalArgumentException("Cannot set crop after view has been laid out"); - } - if (!allowCrop && mAllowCrop) { - throw new IllegalArgumentException("Cannot unset crop mode"); - } - mAllowCrop = allowCrop; - } - - /** - * Gets a bitmap of the cropped region. If cropping is not enabled, returns {@code null}. - */ - public Bitmap getCroppedPhoto() { - if (!mAllowCrop) { - return null; - } - - final Bitmap croppedBitmap = Bitmap.createBitmap( - (int) CROPPED_SIZE, (int) CROPPED_SIZE, Bitmap.Config.ARGB_8888); - final Canvas croppedCanvas = new Canvas(croppedBitmap); - - // scale for the final dimensions - final int cropWidth = mCropRect.right - mCropRect.left; - final float scaleWidth = CROPPED_SIZE / cropWidth; - final float scaleHeight = CROPPED_SIZE / cropWidth; - - // translate to the origin & scale - final Matrix matrix = new Matrix(mDrawMatrix); - matrix.postTranslate(-mCropRect.left, -mCropRect.top); - matrix.postScale(scaleWidth, scaleHeight); - - // draw the photo - if (mDrawable != null) { - croppedCanvas.concat(matrix); - mDrawable.draw(croppedCanvas); - } - return croppedBitmap; - } - - /** - * Resets the image transformation to its original value. - */ - public void resetTransformations() { - // snap transformations; we don't animate - mMatrix.set(mOriginalMatrix); - - // Invalidate the view because if you move off this PhotoView - // to another one and come back, you want it to draw from scratch - // in case you were zoomed in or translated (since those settings - // are not preserved and probably shouldn't be). - invalidate(); - } - - /** - * Rotates the image 90 degrees, clockwise. - */ - public void rotateClockwise() { - rotate(90, true); - } - - /** - * Rotates the image 90 degrees, counter clockwise. - */ - public void rotateCounterClockwise() { - rotate(-90, true); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - // draw the photo - if (mDrawable != null) { - int saveCount = canvas.getSaveCount(); - canvas.save(); - - if (mDrawMatrix != null) { - canvas.concat(mDrawMatrix); - } - mDrawable.draw(canvas); - - canvas.restoreToCount(saveCount); - - if (mVideoBlob != null) { - final Bitmap videoImage = (mVideoReady ? sVideoImage : sVideoNotReadyImage); - final int drawLeft = (getWidth() - videoImage.getWidth()) / 2; - final int drawTop = (getHeight() - videoImage.getHeight()) / 2; - canvas.drawBitmap(videoImage, drawLeft, drawTop, null); - } - - // Extract the drawable's bounds (in our own copy, to not alter the image) - mTranslateRect.set(mDrawable.getBounds()); - if (mDrawMatrix != null) { - mDrawMatrix.mapRect(mTranslateRect); - } - - if (mAllowCrop) { - int previousSaveCount = canvas.getSaveCount(); - canvas.drawRect(0, 0, getWidth(), getHeight(), sCropDimPaint); - canvas.save(); - canvas.clipRect(mCropRect); - - if (mDrawMatrix != null) { - canvas.concat(mDrawMatrix); - } - - mDrawable.draw(canvas); - canvas.restoreToCount(previousSaveCount); - canvas.drawRect(mCropRect, sCropPaint); - } - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - mHaveLayout = true; - final int layoutWidth = getWidth(); - final int layoutHeight = getHeight(); - - if (mAllowCrop) { - mCropSize = Math.min(sCropSize, Math.min(layoutWidth, layoutHeight)); - final int cropLeft = (layoutWidth - mCropSize) / 2; - final int cropTop = (layoutHeight - mCropSize) / 2; - final int cropRight = cropLeft + mCropSize; - final int cropBottom = cropTop + mCropSize; - - // Create a crop region overlay. We need a separate canvas to be able to "punch - // a hole" through to the underlying image. - mCropRect.set(cropLeft, cropTop, cropRight, cropBottom); - } - configureBounds(changed); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (mFixedHeight != -1) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mFixedHeight, - MeasureSpec.AT_MOST)); - setMeasuredDimension(getMeasuredWidth(), mFixedHeight); - } else { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - } - - /** - * Forces a fixed height for this view. - * - * @param fixedHeight The height. If {@code -1}, use the measured height. - */ - public void setFixedHeight(int fixedHeight) { - final boolean adjustBounds = (fixedHeight != mFixedHeight); - mFixedHeight = fixedHeight; - setMeasuredDimension(getMeasuredWidth(), mFixedHeight); - if (adjustBounds) { - configureBounds(true); - requestLayout(); - } - } - - /** - * Enable or disable image transformations. When transformations are enabled, this view - * consumes all touch events. - */ - public void enableImageTransforms(boolean enable) { - mTransformsEnabled = enable; - if (!mTransformsEnabled) { - resetTransformations(); - } - } - - /** - * Configures the bounds of the photo. The photo will always be scaled to fit center. - */ - private void configureBounds(boolean changed) { - if (mDrawable == null || !mHaveLayout) { - return; - } - final int dwidth = mDrawable.getIntrinsicWidth(); - final int dheight = mDrawable.getIntrinsicHeight(); - - final int vwidth = getWidth(); - final int vheight = getHeight(); - - final boolean fits = (dwidth < 0 || vwidth == dwidth) && - (dheight < 0 || vheight == dheight); - - // We need to do the scaling ourself, so have the drawable use its native size. - mDrawable.setBounds(0, 0, dwidth, dheight); - - // Create a matrix with the proper transforms - if (changed || (mMinScale == 0 && mDrawable != null && mHaveLayout)) { - generateMatrix(); - generateScale(); - } - - if (fits || mMatrix.isIdentity()) { - // The bitmap fits exactly, no transform needed. - mDrawMatrix = null; - } else { - mDrawMatrix = mMatrix; - } - } - - /** - * Generates the initial transformation matrix for drawing. Additionally, it sets the - * minimum and maximum scale values. - */ - private void generateMatrix() { - final int dwidth = mDrawable.getIntrinsicWidth(); - final int dheight = mDrawable.getIntrinsicHeight(); - - final int vwidth = mAllowCrop ? sCropSize : getWidth(); - final int vheight = mAllowCrop ? sCropSize : getHeight(); - - final boolean fits = (dwidth < 0 || vwidth == dwidth) && - (dheight < 0 || vheight == dheight); - - if (fits && !mAllowCrop) { - mMatrix.reset(); - } else { - // Generate the required transforms for the photo - mTempSrc.set(0, 0, dwidth, dheight); - if (mAllowCrop) { - mTempDst.set(mCropRect); - } else { - mTempDst.set(0, 0, vwidth, vheight); - } - - if (dwidth < vwidth && dheight < vheight && !mAllowCrop) { - mMatrix.setTranslate(vwidth / 2 - dwidth / 2, vheight / 2 - dheight / 2); - } else { - mMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER); - } - } - mOriginalMatrix.set(mMatrix); - } - - private void generateScale() { - final int dwidth = mDrawable.getIntrinsicWidth(); - final int dheight = mDrawable.getIntrinsicHeight(); - - final int vwidth = mAllowCrop ? getCropSize() : getWidth(); - final int vheight = mAllowCrop ? getCropSize() : getHeight(); - - if (dwidth < vwidth && dheight < vheight && !mAllowCrop) { - mMinScale = 1.0f; - } else { - mMinScale = getScale(); - } - mMaxScale = Math.max(mMinScale * 8, 8); - } - - /** - * @return the size of the crop regions - */ - private int getCropSize() { - return mCropSize > 0 ? mCropSize : sCropSize; - } - - /** - * Returns the currently applied scale factor for the image. - * <p> - * NOTE: This method overwrites any values stored in {@link #mValues}. - */ - private float getScale() { - mMatrix.getValues(mValues); - return mValues[Matrix.MSCALE_X]; - } - - /** - * Scales the image while keeping the aspect ratio. - * - * The given scale is capped so that the resulting scale of the image always remains - * between {@link #mMinScale} and {@link #mMaxScale}. - * - * The scaled image is never allowed to be outside of the viewable area. If the image - * is smaller than the viewable area, it will be centered. - * - * @param newScale the new scale - * @param centerX the center horizontal point around which to scale - * @param centerY the center vertical point around which to scale - */ - private void scale(float newScale, float centerX, float centerY) { - // rotate back to the original orientation - mMatrix.postRotate(-mRotation, getWidth() / 2, getHeight() / 2); - - // ensure that mMixScale <= newScale <= mMaxScale - newScale = Math.max(newScale, mMinScale); - newScale = Math.min(newScale, mMaxScale); - - float currentScale = getScale(); - float factor = newScale / currentScale; - - // apply the scale factor - mMatrix.postScale(factor, factor, centerX, centerY); - - // ensure the image is within the view bounds - snap(); - - // re-apply any rotation - mMatrix.postRotate(mRotation, getWidth() / 2, getHeight() / 2); - - invalidate(); - } - - /** - * Translates the image. - * - * This method will not allow the image to be translated outside of the visible area. - * - * @param tx how many pixels to translate horizontally - * @param ty how many pixels to translate vertically - * @return {@code true} if the translation was applied as specified. Otherwise, {@code false} - * if the translation was modified. - */ - private boolean translate(float tx, float ty) { - mTranslateRect.set(mTempSrc); - mMatrix.mapRect(mTranslateRect); - - final float maxLeft = mAllowCrop ? mCropRect.left : 0.0f; - final float maxRight = mAllowCrop ? mCropRect.right : getWidth(); - float l = mTranslateRect.left; - float r = mTranslateRect.right; - - final float translateX; - if (mAllowCrop) { - // If we're cropping, allow the image to scroll off the edge of the screen - translateX = Math.max(maxLeft - mTranslateRect.right, - Math.min(maxRight - mTranslateRect.left, tx)); - } else { - // Otherwise, ensure the image never leaves the screen - if (r - l < maxRight - maxLeft) { - translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2; - } else { - translateX = Math.max(maxRight - r, Math.min(maxLeft - l, tx)); - } - } - - float maxTop = mAllowCrop ? mCropRect.top: 0.0f; - float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight(); - float t = mTranslateRect.top; - float b = mTranslateRect.bottom; - - final float translateY; - - if (mAllowCrop) { - // If we're cropping, allow the image to scroll off the edge of the screen - translateY = Math.max(maxTop - mTranslateRect.bottom, - Math.min(maxBottom - mTranslateRect.top, ty)); - } else { - // Otherwise, ensure the image never leaves the screen - if (b - t < maxBottom - maxTop) { - translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2; - } else { - translateY = Math.max(maxBottom - b, Math.min(maxTop - t, ty)); - } - } - - // Do the translation - mMatrix.postTranslate(translateX, translateY); - invalidate(); - - return (translateX == tx) && (translateY == ty); - } - - /** - * Snaps the image so it touches all edges of the view. - */ - private void snap() { - mTranslateRect.set(mTempSrc); - mMatrix.mapRect(mTranslateRect); - - // Determine how much to snap in the horizontal direction [if any] - float maxLeft = mAllowCrop ? mCropRect.left : 0.0f; - float maxRight = mAllowCrop ? mCropRect.right : getWidth(); - float l = mTranslateRect.left; - float r = mTranslateRect.right; - - final float translateX; - if (r - l < maxRight - maxLeft) { - // Image is narrower than view; translate to the center of the view - translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2; - } else if (l > maxLeft) { - // Image is off right-edge of screen; bring it into view - translateX = maxLeft - l; - } else if (r < maxRight) { - // Image is off left-edge of screen; bring it into view - translateX = maxRight - r; - } else { - translateX = 0.0f; - } - - // Determine how much to snap in the vertical direction [if any] - float maxTop = mAllowCrop ? mCropRect.top : 0.0f; - float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight(); - float t = mTranslateRect.top; - float b = mTranslateRect.bottom; - - final float translateY; - if (b - t < maxBottom - maxTop) { - // Image is shorter than view; translate to the bottom edge of the view - translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2; - } else if (t > maxTop) { - // Image is off bottom-edge of screen; bring it into view - translateY = maxTop - t; - } else if (b < maxBottom) { - // Image is off top-edge of screen; bring it into view - translateY = maxBottom - b; - } else { - translateY = 0.0f; - } - - if (Math.abs(translateX) > SNAP_THRESHOLD || Math.abs(translateY) > SNAP_THRESHOLD) { - mSnapRunnable.start(translateX, translateY); - } else { - mMatrix.postTranslate(translateX, translateY); - invalidate(); - } - } - - /** - * Rotates the image, either instantly or gradually - * - * @param degrees how many degrees to rotate the image, positive rotates clockwise - * @param animate if {@code true}, animate during the rotation. Otherwise, snap rotate. - */ - private void rotate(float degrees, boolean animate) { - if (animate) { - mRotateRunnable.start(degrees); - } else { - mRotation += degrees; - mMatrix.postRotate(degrees, getWidth() / 2, getHeight() / 2); - invalidate(); - } - } - - /** - * Initializes the header and any static values - */ - private void initialize() { - Context context = getContext(); - - if (!sInitialized) { - sInitialized = true; - - Resources resources = context.getApplicationContext().getResources(); - - sCropSize = resources.getDimensionPixelSize(R.dimen.photo_crop_width); - - sCropDimPaint = new Paint(); - sCropDimPaint.setAntiAlias(true); - sCropDimPaint.setColor(resources.getColor(R.color.photo_crop_dim_color)); - sCropDimPaint.setStyle(Style.FILL); - - sCropPaint = new Paint(); - sCropPaint.setAntiAlias(true); - sCropPaint.setColor(resources.getColor(R.color.photo_crop_highlight_color)); - sCropPaint.setStyle(Style.STROKE); - sCropPaint.setStrokeWidth(resources.getDimension(R.dimen.photo_crop_stroke_width)); - } - - mGestureDetector = new GestureDetectorCompat(context, this, null); - mScaleGetureDetector = new ScaleGestureDetector(context, this); - mScaleRunnable = new ScaleRunnable(this); - mTranslateRunnable = new TranslateRunnable(this); - mSnapRunnable = new SnapRunnable(this); - mRotateRunnable = new RotateRunnable(this); - } - - /** - * Runnable that animates an image scale operation. - */ - private static class ScaleRunnable implements Runnable { - - private final PhotoView mHeader; - - private float mCenterX; - private float mCenterY; - - private boolean mZoomingIn; - - private float mTargetScale; - private float mStartScale; - private float mVelocity; - private long mStartTime; - - private boolean mRunning; - private boolean mStop; - - public ScaleRunnable(PhotoView header) { - mHeader = header; - } - - /** - * Starts the animation. There is no target scale bounds check. - */ - public boolean start(float startScale, float targetScale, float centerX, float centerY) { - if (mRunning) { - return false; - } - - mCenterX = centerX; - mCenterY = centerY; - - // Ensure the target scale is within the min/max bounds - mTargetScale = targetScale; - mStartTime = System.currentTimeMillis(); - mStartScale = startScale; - mZoomingIn = mTargetScale > mStartScale; - mVelocity = (mTargetScale - mStartScale) / ZOOM_ANIMATION_DURATION; - mRunning = true; - mStop = false; - mHeader.post(this); - return true; - } - - /** - * Stops the animation in place. It does not snap the image to its final zoom. - */ - public void stop() { - mRunning = false; - mStop = true; - } - - @Override - public void run() { - if (mStop) { - return; - } - - // Scale - long now = System.currentTimeMillis(); - long ellapsed = now - mStartTime; - float newScale = (mStartScale + mVelocity * ellapsed); - mHeader.scale(newScale, mCenterX, mCenterY); - - // Stop when done - if (newScale == mTargetScale || (mZoomingIn == (newScale > mTargetScale))) { - mHeader.scale(mTargetScale, mCenterX, mCenterY); - stop(); - } - - if (!mStop) { - mHeader.post(this); - } - } - } - - /** - * Runnable that animates an image translation operation. - */ - private static class TranslateRunnable implements Runnable { - - private static final float DECELERATION_RATE = 1000f; - private static final long NEVER = -1L; - - private final PhotoView mHeader; - - private float mVelocityX; - private float mVelocityY; - - private long mLastRunTime; - private boolean mRunning; - private boolean mStop; - - public TranslateRunnable(PhotoView header) { - mLastRunTime = NEVER; - mHeader = header; - } - - /** - * Starts the animation. - */ - public boolean start(float velocityX, float velocityY) { - if (mRunning) { - return false; - } - mLastRunTime = NEVER; - mVelocityX = velocityX; - mVelocityY = velocityY; - mStop = false; - mRunning = true; - mHeader.post(this); - return true; - } - - /** - * Stops the animation in place. It does not snap the image to its final translation. - */ - public void stop() { - mRunning = false; - mStop = true; - } - - @Override - public void run() { - // See if we were told to stop: - if (mStop) { - return; - } - - // Translate according to current velocities and time delta: - long now = System.currentTimeMillis(); - float delta = (mLastRunTime != NEVER) ? (now - mLastRunTime) / 1000f : 0f; - final boolean didTranslate = mHeader.translate(mVelocityX * delta, mVelocityY * delta); - mLastRunTime = now; - // Slow down: - float slowDown = DECELERATION_RATE * delta; - if (mVelocityX > 0f) { - mVelocityX -= slowDown; - if (mVelocityX < 0f) { - mVelocityX = 0f; - } - } else { - mVelocityX += slowDown; - if (mVelocityX > 0f) { - mVelocityX = 0f; - } - } - if (mVelocityY > 0f) { - mVelocityY -= slowDown; - if (mVelocityY < 0f) { - mVelocityY = 0f; - } - } else { - mVelocityY += slowDown; - if (mVelocityY > 0f) { - mVelocityY = 0f; - } - } - - // Stop when done - if ((mVelocityX == 0f && mVelocityY == 0f) || !didTranslate) { - stop(); - mHeader.snap(); - } - - // See if we need to continue flinging: - if (mStop) { - return; - } - mHeader.post(this); - } - } - - /** - * Runnable that animates an image translation operation. - */ - private static class SnapRunnable implements Runnable { - - private static final long NEVER = -1L; - - private final PhotoView mHeader; - - private float mTranslateX; - private float mTranslateY; - - private long mStartRunTime; - private boolean mRunning; - private boolean mStop; - - public SnapRunnable(PhotoView header) { - mStartRunTime = NEVER; - mHeader = header; - } - - /** - * Starts the animation. - */ - public boolean start(float translateX, float translateY) { - if (mRunning) { - return false; - } - mStartRunTime = NEVER; - mTranslateX = translateX; - mTranslateY = translateY; - mStop = false; - mRunning = true; - mHeader.postDelayed(this, SNAP_DELAY); - return true; - } - - /** - * Stops the animation in place. It does not snap the image to its final translation. - */ - public void stop() { - mRunning = false; - mStop = true; - } - - @Override - public void run() { - // See if we were told to stop: - if (mStop) { - return; - } - - // Translate according to current velocities and time delta: - long now = System.currentTimeMillis(); - float delta = (mStartRunTime != NEVER) ? (now - mStartRunTime) : 0f; - - if (mStartRunTime == NEVER) { - mStartRunTime = now; - } - - float transX; - float transY; - if (delta >= SNAP_DURATION) { - transX = mTranslateX; - transY = mTranslateY; - } else { - transX = (mTranslateX / (SNAP_DURATION - delta)) * 10f; - transY = (mTranslateY / (SNAP_DURATION - delta)) * 10f; - if (Math.abs(transX) > Math.abs(mTranslateX) || transX == Float.NaN) { - transX = mTranslateX; - } - if (Math.abs(transY) > Math.abs(mTranslateY) || transY == Float.NaN) { - transY = mTranslateY; - } - } - - mHeader.translate(transX, transY); - mTranslateX -= transX; - mTranslateY -= transY; - - if (mTranslateX == 0 && mTranslateY == 0) { - stop(); - } - - // See if we need to continue flinging: - if (mStop) { - return; - } - mHeader.post(this); - } - } - - /** - * Runnable that animates an image rotation operation. - */ - private static class RotateRunnable implements Runnable { - - private static final long NEVER = -1L; - - private final PhotoView mHeader; - - private float mTargetRotation; - private float mAppliedRotation; - private float mVelocity; - private long mLastRuntime; - - private boolean mRunning; - private boolean mStop; - - public RotateRunnable(PhotoView header) { - mHeader = header; - } - - /** - * Starts the animation. - */ - public void start(float rotation) { - if (mRunning) { - return; - } - - mTargetRotation = rotation; - mVelocity = mTargetRotation / ROTATE_ANIMATION_DURATION; - mAppliedRotation = 0f; - mLastRuntime = NEVER; - mStop = false; - mRunning = true; - mHeader.post(this); - } - - /** - * Stops the animation in place. It does not snap the image to its final rotation. - */ - public void stop() { - mRunning = false; - mStop = true; - } - - @Override - public void run() { - if (mStop) { - return; - } - - if (mAppliedRotation != mTargetRotation) { - long now = System.currentTimeMillis(); - long delta = mLastRuntime != NEVER ? now - mLastRuntime : 0L; - float rotationAmount = mVelocity * delta; - if (mAppliedRotation < mTargetRotation - && mAppliedRotation + rotationAmount > mTargetRotation - || mAppliedRotation > mTargetRotation - && mAppliedRotation + rotationAmount < mTargetRotation) { - rotationAmount = mTargetRotation - mAppliedRotation; - } - mHeader.rotate(rotationAmount, false); - mAppliedRotation += rotationAmount; - if (mAppliedRotation == mTargetRotation) { - stop(); - } - mLastRuntime = now; - } - - if (mStop) { - return; - } - mHeader.post(this); - } - } -} diff --git a/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java b/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java deleted file mode 100644 index 2669273..0000000 --- a/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. - * Licensed to The Android Open Source Project. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ex.photo.views; - -import android.view.View; -import android.widget.ProgressBar; - -/** - * This class wraps around two progress bars and is solely designed to fix - * a bug in the framework (b/6928449) that prevents a progress bar from - * gracefully switching back and forth between indeterminate and determinate - * modes. - */ -public class ProgressBarWrapper { - private final ProgressBar mDeterminate; - private final ProgressBar mIndeterminate; - private boolean mIsIndeterminate; - - public ProgressBarWrapper(ProgressBar determinate, - ProgressBar indeterminate, boolean isIndeterminate) { - mDeterminate = determinate; - mIndeterminate = indeterminate; - setIndeterminate(isIndeterminate); - } - - public void setIndeterminate(boolean isIndeterminate) { - mIsIndeterminate = isIndeterminate; - - setVisibility(mIsIndeterminate); - } - - public void setVisibility(int visibility) { - if (visibility == View.INVISIBLE || visibility == View.GONE) { - mIndeterminate.setVisibility(visibility); - mDeterminate.setVisibility(visibility); - } else { - setVisibility(mIsIndeterminate); - } - } - - private void setVisibility(boolean isIndeterminate) { - mIndeterminate.setVisibility(isIndeterminate ? View.VISIBLE : View.GONE); - mDeterminate.setVisibility(isIndeterminate ? View.GONE : View.VISIBLE); - } - - public void setMax(int max) { - mDeterminate.setMax(max); - } - - public void setProgress(int progress) { - mDeterminate.setProgress(progress); - } -} diff --git a/variablespeed/jni/Android.mk b/variablespeed/jni/Android.mk index 66ec1c5..08de535 100644 --- a/variablespeed/jni/Android.mk +++ b/variablespeed/jni/Android.mk @@ -44,6 +44,7 @@ LOCAL_SHARED_LIBRARIES := \ libstlport \ libutils \ libcutils \ + liblog \ LOCAL_LDLIBS := \ -lOpenSLES \ |