Java tutorial
/* * Copyright (C) 2014 Andrew Comminos * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.morlunk.mumbleclient.channel; import android.app.Activity; import android.app.SearchManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.CursorWrapper; import android.graphics.PorterDuff; import android.media.AudioManager; import android.os.Bundle; import android.os.RemoteException; import android.preference.PreferenceManager; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.view.ActionMode; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.morlunk.jumble.IJumbleService; import com.morlunk.jumble.model.IChannel; import com.morlunk.jumble.model.IUser; import com.morlunk.jumble.util.IJumbleObserver; import com.morlunk.jumble.util.JumbleException; import com.morlunk.jumble.util.JumbleObserver; import com.morlunk.mumbleclient.R; import com.morlunk.mumbleclient.Settings; import com.morlunk.mumbleclient.db.DatabaseProvider; import com.morlunk.mumbleclient.util.JumbleServiceFragment; public class ChannelListFragment extends JumbleServiceFragment implements OnChannelClickListener, OnUserClickListener, SharedPreferences.OnSharedPreferenceChangeListener { private IJumbleObserver mServiceObserver = new JumbleObserver() { @Override public void onDisconnected(JumbleException e) { mChannelView.setAdapter(null); } @Override public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) { mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); if (getService().getSession() == user.getSession()) { scrollToChannel(newChannel.getId()); } } @Override public void onChannelAdded(IChannel channel) { mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); } @Override public void onChannelRemoved(IChannel channel) { mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); } @Override public void onChannelStateUpdated(IChannel channel) { mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); } @Override public void onUserConnected(IUser user) { mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); } @Override public void onUserRemoved(IUser user, String reason) { // If we are the user being removed, don't update the channel list. // We won't be in a synchronized state. if (!getService().isSynchronized()) return; mChannelListAdapter.updateChannels(); mChannelListAdapter.notifyDataSetChanged(); } @Override public void onUserStateUpdated(IUser user) { mChannelListAdapter.animateUserStateUpdate(user, mChannelView); getActivity().supportInvalidateOptionsMenu(); // Update self mute/deafen state } @Override public void onUserTalkStateUpdated(IUser user) { mChannelListAdapter.animateUserTalkStateUpdate(user, mChannelView); } }; private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (getActivity() != null) getActivity().supportInvalidateOptionsMenu(); // Update bluetooth menu item } }; private RecyclerView mChannelView; private ChannelListAdapter mChannelListAdapter; private ChatTargetProvider mTargetProvider; private DatabaseProvider mDatabaseProvider; private ActionMode mActionMode; private Settings mSettings; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mTargetProvider = (ChatTargetProvider) getParentFragment(); } catch (ClassCastException e) { throw new ClassCastException(getParentFragment().toString() + " must implement ChatTargetProvider"); } try { mDatabaseProvider = (DatabaseProvider) getActivity(); } catch (ClassCastException e) { throw new ClassCastException(getActivity().toString() + " must implement DatabaseProvider"); } mSettings = Settings.getInstance(activity); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); preferences.registerOnSharedPreferenceChangeListener(this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_channel_list, container, false); mChannelView = (RecyclerView) view.findViewById(R.id.channelUsers); mChannelView.setLayoutManager(new LinearLayoutManager(getActivity())); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); registerForContextMenu(mChannelView); getActivity().registerReceiver(mBluetoothReceiver, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED)); } @Override public void onDetach() { getActivity().unregisterReceiver(mBluetoothReceiver); super.onDetach(); } @Override public void onDestroy() { super.onDestroy(); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); preferences.unregisterOnSharedPreferenceChangeListener(this); } @Override public IJumbleObserver getServiceObserver() { return mServiceObserver; } @Override public void onServiceBound(IJumbleService service) { try { if (mChannelListAdapter == null) { setupChannelList(); } else { mChannelListAdapter.setService(service); } } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem muteItem = menu.findItem(R.id.menu_mute_button); MenuItem deafenItem = menu.findItem(R.id.menu_deafen_button); if (getService() != null && getService().isSynchronized()) { // Color the action bar icons to the primary text color of the theme, TODO move this elsewhere int foregroundColor = getActivity().getTheme() .obtainStyledAttributes(new int[] { android.R.attr.textColorPrimaryInverse }).getColor(0, -1); IUser self = getService().getSessionUser(); muteItem.setIcon( self.isSelfMuted() ? R.drawable.ic_action_microphone_muted : R.drawable.ic_action_microphone); deafenItem .setIcon(self.isSelfDeafened() ? R.drawable.ic_action_audio_muted : R.drawable.ic_action_audio); muteItem.getIcon().mutate().setColorFilter(foregroundColor, PorterDuff.Mode.MULTIPLY); deafenItem.getIcon().mutate().setColorFilter(foregroundColor, PorterDuff.Mode.MULTIPLY); MenuItem bluetoothItem = menu.findItem(R.id.menu_bluetooth); bluetoothItem.setChecked(getService().usingBluetoothSco()); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.fragment_channel_list, menu); MenuItem searchItem = menu.findItem(R.id.menu_search); SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { @Override public boolean onSuggestionSelect(int i) { return false; } @Override public boolean onSuggestionClick(int i) { CursorWrapper cursor = (CursorWrapper) searchView.getSuggestionsAdapter().getItem(i); int typeColumn = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); int dataIdColumn = cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA); String itemType = cursor.getString(typeColumn); int itemId = cursor.getInt(dataIdColumn); if (ChannelSearchProvider.INTENT_DATA_CHANNEL.equals(itemType)) { if (getService().getSessionChannel().getId() != itemId) { getService().joinChannel(itemId); } else { scrollToChannel(itemId); } return true; } else if (ChannelSearchProvider.INTENT_DATA_USER.equals(itemType)) { scrollToUser(itemId); return true; } return false; } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_mute_button: { IUser self = getService().getSessionUser(); boolean muted = !self.isSelfMuted(); boolean deafened = self.isSelfDeafened(); deafened &= muted; // Undeafen if mute is off getService().setSelfMuteDeafState(muted, deafened); getActivity().supportInvalidateOptionsMenu(); return true; } case R.id.menu_deafen_button: { IUser self = getService().getSessionUser(); boolean deafened = !self.isSelfDeafened(); getService().setSelfMuteDeafState(deafened, deafened); getActivity().supportInvalidateOptionsMenu(); return true; } case R.id.menu_search: return false; case R.id.menu_bluetooth: item.setChecked(!item.isChecked()); if (item.isChecked()) { getService().enableBluetoothSco(); } else { getService().disableBluetoothSco(); } return true; } return super.onOptionsItemSelected(item); } private void setupChannelList() throws RemoteException { mChannelListAdapter = new ChannelListAdapter(getActivity(), getService(), mDatabaseProvider.getDatabase(), getChildFragmentManager(), isShowingPinnedChannels(), mSettings.shouldShowUserCount()); mChannelListAdapter.setOnChannelClickListener(this); mChannelListAdapter.setOnUserClickListener(this); mChannelView.setAdapter(mChannelListAdapter); mChannelListAdapter.notifyDataSetChanged(); } /** * Scrolls to the passed channel. */ public void scrollToChannel(int channelId) { int channelPosition = mChannelListAdapter.getChannelPosition(channelId); mChannelView.smoothScrollToPosition(channelPosition); } /** * Scrolls to the passed user. */ public void scrollToUser(int userId) { int userPosition = mChannelListAdapter.getUserPosition(userId); mChannelView.smoothScrollToPosition(userPosition); } private boolean isShowingPinnedChannels() { return getArguments().getBoolean("pinned"); } @Override public void onChannelClick(IChannel channel) { if (mTargetProvider.getChatTarget() != null && channel.equals(mTargetProvider.getChatTarget().getChannel()) && mActionMode != null) { // Dismiss action mode if double pressed. FIXME: use list view selection instead? mActionMode.finish(); } else { ActionMode.Callback cb = new ChatTargetActionModeCallback(mTargetProvider, new ChatTargetProvider.ChatTarget(channel)) { @Override public void onDestroyActionMode(ActionMode actionMode) { super.onDestroyActionMode(actionMode); mActionMode = null; } }; mActionMode = ((ActionBarActivity) getActivity()).startSupportActionMode(cb); } } @Override public void onUserClick(IUser user) { if (mTargetProvider.getChatTarget() != null && user.equals(mTargetProvider.getChatTarget().getUser()) && mActionMode != null) { // Dismiss action mode if double pressed. FIXME: use list view selection instead? mActionMode.finish(); } else { ActionMode.Callback cb = new ChatTargetActionModeCallback(mTargetProvider, new ChatTargetProvider.ChatTarget(user)) { @Override public void onDestroyActionMode(ActionMode actionMode) { super.onDestroyActionMode(actionMode); mActionMode = null; } }; mActionMode = ((ActionBarActivity) getActivity()).startSupportActionMode(cb); } } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (Settings.PREF_SHOW_USER_COUNT.equals(key) && mChannelListAdapter != null) { mChannelListAdapter.setShowChannelUserCount(mSettings.shouldShowUserCount()); } } }