/* * Copyright (C) 2012 Simon Robinson * * This file is part of Com-Me. * * Com-Me is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * Com-Me is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Com-Me. * If not, see <http://www.gnu.org/licenses/>. */ package ac.robinson.mediatablet.activity; import java.lang.reflect.Method; import java.util.ArrayList; import ac.robinson.mediatablet.MediaTablet; import ac.robinson.mediatablet.MediaTabletActivity; import ac.robinson.mediatablet.MediaViewerActivity; import ac.robinson.mediatablet.R; import ac.robinson.mediatablet.provider.HomesteadItem; import ac.robinson.mediatablet.provider.HomesteadManager; import ac.robinson.mediatablet.provider.MediaItem; import ac.robinson.mediatablet.provider.PersonAdapter; import ac.robinson.mediatablet.provider.PersonItem; import ac.robinson.mediatablet.provider.PersonManager; import ac.robinson.mediatablet.view.ColorPickerDialog; import ac.robinson.mediatablet.view.PersonViewHolder; import ac.robinson.util.ImageCacheUtilities; import ac.robinson.util.UIUtilities; import ac.robinson.util.UIUtilities.ReflectionTab; import ac.robinson.util.UIUtilities.ReflectionTabListener; import ac.robinson.view.CrossFadeDrawable; import ac.robinson.view.FastBitmapDrawable; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.Button; import android.widget.GridView; public class PeopleBrowserActivity extends MediaTabletActivity { private PersonAdapter mPersonAdapter; private FastBitmapDrawable mDefaultIcon; private GridView mGrid; private final Handler mScrollHandler = new ScrollHandler(); private int mScrollState = ScrollManager.SCROLL_STATE_IDLE; private boolean mPendingIconsUpdate; private boolean mFingerUp = true; private String mParentId; // the homestead of these, or null if it's the sharing selection view @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // load previous position and parent on screen rotation mParentId = null; if (savedInstanceState != null) { mParentId = savedInstanceState.getString(getString(R.string.extra_parent_id)); } else { final Intent intent = getIntent(); if (intent != null) { mParentId = intent.getStringExtra(getString(R.string.extra_parent_id)); String personId = intent.getStringExtra(getString(R.string.extra_internal_id)); if (personId != null) { onView(personId); } } } if (mParentId != null) { UIUtilities.addActionBarTabs(this, new ReflectionTab[] { new ReflectionTab(R.id.intent_homestead_browser, R.drawable.ic_menu_homesteads, getString(R.string.title_homestead_browser)), new ReflectionTab(R.id.intent_people_browser, R.drawable.ic_menu_people, getString(R.string.title_people_browser), true) }, mReflectionTabListener); } else { UIUtilities.addActionBarTabs(this, new ReflectionTab[] { new ReflectionTab(R.id.intent_people_browser, R.drawable.ic_menu_people, getString(R.string.title_people_browser_selection), true) }, null); } UIUtilities.configureActionBar(this, true, false, R.string.title_people_browser, 0); setContentView(R.layout.people_browser); initialisePeopleGridView(); } @Override public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putString(getString(R.string.extra_parent_id), mParentId); super.onSaveInstanceState(savedInstanceState); } @Override protected void onPause() { super.onPause(); mScrollHandler.removeMessages(MediaTablet.MESSAGE_UPDATE_GRID_ICONS); } @Override protected void onDestroy() { ImageCacheUtilities.cleanupCache(); super.onDestroy(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); if (mParentId == null) { inflater.inflate(R.menu.cancel, menu); inflater.inflate(R.menu.share, menu); } else { inflater.inflate(R.menu.public_media, menu); inflater.inflate(R.menu.add_user, menu); try { Method setShowAsAction = MenuItem.class.getMethod("setShowAsAction", Integer.TYPE); setShowAsAction.invoke(menu.findItem(R.id.menu_add_user), MenuItem.class.getField("SHOW_AS_ACTION_NEVER").getInt(null)); } catch (Exception e) { // platform is probably < 11 } } inflater.inflate(R.menu.people_preferences, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); // quicker than clearing top return true; case R.id.menu_cancel: finish(); return true; case R.id.menu_share: shareToPeople(); return true; case R.id.menu_view_public_media: viewPublicMedia(); return true; case R.id.menu_add_user: editPerson(mParentId, null); return true; case R.id.menu_people_change_colour: editHomesteadColour(); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void loadPreferences(SharedPreferences mediaTabletSettings) { } @Override protected String getCurrentPersonId() { return PersonItem.UNKNOWN_PERSON_ID; } private void initialisePeopleGridView() { // for API 11 and above, buttons are in the action bar - could use XML-v11 but maintenance is a hassle if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { findViewById(R.id.panel_people).setVisibility(View.GONE); } if (mParentId == null) { findViewById(R.id.button_people_add_user).setVisibility(View.GONE); findViewById(R.id.button_people_change_colour).setVisibility(View.GONE); ((Button) findViewById(R.id.button_people_view_homesteads)).setCompoundDrawablesWithIntrinsicBounds(0, android.R.drawable.ic_menu_revert, 0, 0); ((Button) findViewById(R.id.button_people_view_public_media)).setCompoundDrawablesWithIntrinsicBounds(0, android.R.drawable.ic_menu_share, 0, 0); } mPersonAdapter = new PersonAdapter(this, mParentId); mDefaultIcon = mPersonAdapter.getDefaultIcon(); mGrid = (GridView) findViewById(R.id.grid_people); final GridView grid = mGrid; grid.setAdapter(mPersonAdapter); grid.setOnScrollListener(new ScrollManager()); grid.setOnTouchListener(new FingerTracker()); grid.setOnItemSelectedListener(new SelectionTracker()); grid.setOnItemClickListener(new PersonViewer()); // TODO: make this work // TextView emptyView = new TextView(this); // emptyView.setText("unknown"); // grid.setEmptyView(emptyView); } private void editHomesteadColour() { HomesteadItem tempHomestead = HomesteadManager.findHomesteadByInternalId(getContentResolver(), mParentId); int currentColour = tempHomestead.getColour(); new ColorPickerDialog(PeopleBrowserActivity.this, mColourChangedListener, currentColour).show(); } private ColorPickerDialog.OnColorChangedListener mColourChangedListener = new ColorPickerDialog.OnColorChangedListener() { @Override public void colorChanged(int colour) { HomesteadItem editedHomestead = HomesteadManager.findHomesteadByInternalId(getContentResolver(), mParentId); editedHomestead.setColour(colour); HomesteadManager.updateHomestead(getContentResolver(), editedHomestead); UIUtilities.showToast(PeopleBrowserActivity.this, R.string.message_colour_changed); } }; private void shareToPeople() { // TODO: currently, if the view is re-created, the selected ids are lost - fix (save in temp prefs?) ArrayList<String> selectedItems = mPersonAdapter.getSelectedItems(); Intent resultIntent = new Intent(PeopleBrowserActivity.this, MediaViewerActivity.class); resultIntent.putExtra(getString(R.string.extra_selected_items), selectedItems.toArray(new String[selectedItems.size()])); setResult(Activity.RESULT_OK, resultIntent); finish(); } public void handleButtonClicks(View currentButton) { final int buttonId = currentButton.getId(); switch (buttonId) { case R.id.button_people_add_user: editPerson(mParentId, null); case R.id.button_people_change_colour: if (mParentId == null) { finish(); } else { editHomesteadColour(); } break; case R.id.button_people_view_homesteads: finish(); break; case R.id.button_people_view_public_media: if (mParentId == null) { shareToPeople(); } else { viewPublicMedia(); } break; } } private ReflectionTabListener mReflectionTabListener = new ReflectionTabListener() { @Override public void onTabSelected(int tabId) { if (tabId == R.id.intent_homestead_browser) { finish(); } } @Override public void onTabReselected(int tabId) { } @Override public void onTabUnselected(int tabId) { } }; public int getScrollState() { return mScrollState; // for PersonAdapter purposes } public boolean isPendingIconsUpdate() { return mPendingIconsUpdate; // for PersonAdapter purposes } /** * Switch to an item's view when it is touched * * @param ownerId the PersonItem.INTERNAL_ID of the person whose media should be shown */ private void onView(String ownerId) { PersonItem person = PersonManager.findPersonByInternalId(getContentResolver(), ownerId); Intent browseMediaIntent = new Intent(PeopleBrowserActivity.this, MediaBrowserActivity.class); browseMediaIntent.putExtra(getString(R.string.extra_parent_id), ownerId); // not really the best place to manage person re-locking, but never mind... if (!person.isLocked()) { if (person.lockExpired()) { person.setLockStatus(PersonItem.PERSON_LOCKED); PersonManager.updatePerson(getContentResolver(), person); } else { browseMediaIntent.putExtra(getString(R.string.extra_media_visibility), MediaItem.MEDIA_PRIVATE); } } else { UIUtilities.showToast(PeopleBrowserActivity.this, R.string.message_view_locked_person, true); } startActivityForResult(browseMediaIntent, R.id.intent_media_browser); } // setBackgroundDrawable is deprecated from API 16+ (Jelly Bean), but we still want to target earlier versions; // since this is purely a name change, there's no real reason to do anything platform-independent @SuppressWarnings("deprecation") private void updatePeopleIcons(boolean fadeIn) { mPendingIconsUpdate = false; final GridView grid = mGrid; final FastBitmapDrawable icon = mDefaultIcon; final int count = grid.getChildCount(); for (int i = 0; i < count; i++) { final View view = grid.getChildAt(i); final PersonViewHolder holder = (PersonViewHolder) view.getTag(); if (holder.queryIcon) { // if the icon has gone missing (recently imported or cache deletion), regenerate it String personCacheId = PersonItem.getCacheId(holder.personInternalId); FastBitmapDrawable cachedIcon = ImageCacheUtilities.getCachedIcon(MediaTablet.DIRECTORY_THUMBS, personCacheId, ImageCacheUtilities.NULL_DRAWABLE); if (ImageCacheUtilities.NULL_DRAWABLE.equals(cachedIcon)) { PersonManager.reloadPersonIcon(getResources(), getContentResolver(), holder.personInternalId); cachedIcon = ImageCacheUtilities.getCachedIcon(MediaTablet.DIRECTORY_THUMBS, personCacheId, icon); } if (fadeIn) { CrossFadeDrawable d = holder.transition; d.setEnd(cachedIcon.getBitmap()); holder.display.setBackgroundDrawable(d); d.startTransition(MediaTablet.ANIMATION_FADE_TRANSITION_DURATION); } else { holder.display.setBackgroundDrawable(cachedIcon); } holder.loader.setVisibility(View.GONE); holder.queryIcon = false; if (holder.selected) { holder.overlay.setBackgroundResource(R.drawable.item_public); holder.overlay.setPadding(0, 0, 0, 0); } else { holder.overlay.setBackgroundDrawable(null); } } } grid.invalidate(); } private void postUpdatePeopleIcons() { Handler handler = mScrollHandler; Message message = handler.obtainMessage(MediaTablet.MESSAGE_UPDATE_GRID_ICONS, PeopleBrowserActivity.this); handler.removeMessages(MediaTablet.MESSAGE_UPDATE_GRID_ICONS); mPendingIconsUpdate = true; handler.sendMessage(message); } private class ScrollManager implements AbsListView.OnScrollListener { public void onScrollStateChanged(AbsListView view, int scrollState) { if (mScrollState == SCROLL_STATE_FLING && scrollState != SCROLL_STATE_FLING) { final Handler handler = mScrollHandler; final Message message = handler.obtainMessage(MediaTablet.MESSAGE_UPDATE_GRID_ICONS, PeopleBrowserActivity.this); handler.removeMessages(MediaTablet.MESSAGE_UPDATE_GRID_ICONS); handler.sendMessageDelayed(message, mFingerUp ? 0 : MediaTablet.ANIMATION_ICON_SHOW_DELAY); mPendingIconsUpdate = true; } else if (scrollState == SCROLL_STATE_FLING) { mPendingIconsUpdate = false; mScrollHandler.removeMessages(MediaTablet.MESSAGE_UPDATE_GRID_ICONS); } mScrollState = scrollState; } // for showing the overlay with current item information - no need in people view public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { return; } } private static class ScrollHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MediaTablet.MESSAGE_UPDATE_GRID_ICONS: ((PeopleBrowserActivity) msg.obj).updatePeopleIcons(true); break; } } } private class FingerTracker implements View.OnTouchListener { public boolean onTouch(View view, MotionEvent event) { final int action = event.getAction(); mFingerUp = action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL; if (mFingerUp && mScrollState != ScrollManager.SCROLL_STATE_FLING) { postUpdatePeopleIcons(); } return false; } } private class SelectionTracker implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { if (mScrollState != ScrollManager.SCROLL_STATE_IDLE) { mScrollState = ScrollManager.SCROLL_STATE_IDLE; postUpdatePeopleIcons(); } } public void onNothingSelected(AdapterView<?> adapterView) { } } private class PersonViewer implements AdapterView.OnItemClickListener { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (mParentId == null) { PersonViewHolder currentHolder = (PersonViewHolder) view.getTag(); if (currentHolder.selected) { mPersonAdapter.setItemNotSelected(currentHolder.personInternalId); currentHolder.selected = false; } else { mPersonAdapter.setItemSelected(currentHolder.personInternalId); currentHolder.selected = true; } currentHolder.queryIcon = true; updatePeopleIcons(false); } else { onView(((PersonViewHolder) view.getTag()).personInternalId); } } } @Override public void onActivityResult(int requestCode, int resultCode, Intent resultIntent) { switch (requestCode) { case R.id.intent_media_browser: if (resultCode == Activity.RESULT_OK && resultIntent != null) { if (resultIntent.getBooleanExtra(getString(R.string.extra_finish_activity), false)) { finish(); } } break; default: super.onActivityResult(requestCode, resultCode, resultIntent); } } }