Java tutorial
/* This file is part of the Android Clementine Remote. * Copyright (C) 2013, Andreas Muttscheller <asfa194@gmail.com> * * 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 de.qspool.clementineremote.ui; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.widget.DrawerLayout; import android.util.Log; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import java.util.LinkedList; import de.qspool.clementineremote.App; import de.qspool.clementineremote.R; import de.qspool.clementineremote.backend.Clementine; import de.qspool.clementineremote.backend.pb.ClementineMessage; import de.qspool.clementineremote.backend.pb.ClementineMessageFactory; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.MsgType; import de.qspool.clementineremote.ui.adapter.SeparatedListAdapter; import de.qspool.clementineremote.ui.fragments.AbstractDrawerFragment; import de.qspool.clementineremote.ui.fragments.DonateFragment; import de.qspool.clementineremote.ui.fragments.DownloadsFragment; import de.qspool.clementineremote.ui.fragments.LibraryFragment; import de.qspool.clementineremote.ui.fragments.PlayerFragment; import de.qspool.clementineremote.ui.fragments.PlaylistFragment; import de.qspool.clementineremote.utils.Utilities; public class MainActivity extends FragmentActivity { private final static String TAG = "MainActivity"; private final static String MENU_POSITION = "last_menu_position"; private SharedPreferences mSharedPref; private MainActivityHandler mHandler; private Toast mToast; private int mCurrentFragment; private LinkedList<AbstractDrawerFragment> mFragments = new LinkedList<>(); private AbstractDrawerFragment mPlayerFragment; private ListView mDrawerList; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private int mLastPosition = 1; private boolean mOpenConnectDialog = true; private boolean mInstanceSaved = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate"); // Keep screen on if user has requested this in preferences if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(App.SP_KEEP_SCREEN_ON, true) && Utilities.isRemoteConnected()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } setContentView(R.layout.main_activity); /* * Define here the available fragments in the mail layout */ mFragments.add(new PlayerFragment()); mFragments.add(new PlaylistFragment()); mFragments.add(new LibraryFragment()); mFragments.add(new DownloadsFragment()); mFragments.add(new DonateFragment()); mDrawerList = (ListView) findViewById(R.id.left_drawer); if (findViewById(R.id.player_frame) != null) { mPlayerFragment = new PlayerFragment(); getSupportFragmentManager().beginTransaction().add(R.id.player_frame, mPlayerFragment).commit(); mLastPosition = 2; } // Create the adapters for the sections ArrayAdapter<String> remoteAdapter = new ArrayAdapter<>(this, R.layout.drawer_list_item, getResources().getStringArray(R.array.navigation_array_remote)); ArrayAdapter<String> settingsAdapter = new ArrayAdapter<>(this, R.layout.drawer_list_item, getResources().getStringArray(R.array.navigation_array_settings)); ArrayAdapter<String> disconnectAdapter = new ArrayAdapter<>(this, R.layout.drawer_list_item, getResources().getStringArray(R.array.navigation_array_disconnect)); // Create the header adapter SeparatedListAdapter separatedListAdapter = new SeparatedListAdapter(this); String[] headers = getResources().getStringArray(R.array.navigation_headers); separatedListAdapter.addSection(headers[0], remoteAdapter); separatedListAdapter.addSection(headers[1], settingsAdapter); separatedListAdapter.addSection(headers[2], disconnectAdapter); mDrawerList.setAdapter(separatedListAdapter); mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ R.string.connectdialog_connect, /* "open drawer" description */ R.string.dialog_close /* "close drawer" description */ ) { }; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); mSharedPref = PreferenceManager.getDefaultSharedPreferences(this); // When we have a download notifitication and it was clicked, show the download. if (getIntent().hasExtra(App.NOTIFICATION_ID)) { int id = getIntent().getIntExtra(App.NOTIFICATION_ID, 0); if (id == -1) { mLastPosition = 1; } else { mLastPosition = 4; } } selectItem(mLastPosition, 0); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); if (savedInstanceState != null && savedInstanceState.containsKey(MENU_POSITION)) { mLastPosition = savedInstanceState.getInt(MENU_POSITION); } Log.d(TAG, "onPostCreate"); } @Override protected void onSaveInstanceState(Bundle outState) { outState.putInt(MENU_POSITION, mLastPosition); mInstanceSaved = true; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); selectItem(mLastPosition, 0); } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume"); // Check if the user has changed the preferences to keep the screen on if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean(App.SP_KEEP_SCREEN_ON, true) && Utilities.isRemoteConnected()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } mOpenConnectDialog = true; mInstanceSaved = false; // Check if we are still connected if (App.mClementineConnection == null || App.mClementine == null || !App.mClementineConnection.isConnected()) { Log.d(TAG, "onResume - disconnect"); setResult(ConnectDialog.RESULT_DISCONNECT); finish(); } else { Log.d(TAG, "onResume - start"); // Set the handler mHandler = new MainActivityHandler(this); App.mClementineConnection.setUiHandler(mHandler); mDrawerList.performItemClick(mDrawerList.getAdapter().getView(mLastPosition, null, null), mLastPosition, mDrawerList.getAdapter().getItemId(mLastPosition)); } } @Override public void onPause() { super.onPause(); Log.d(TAG, "onPause"); mHandler = null; if (App.mClementineConnection != null) { App.mClementineConnection.setUiHandler(null); } } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); // If we disconnected, open connectdialog if (App.mClementineConnection == null || App.mClementine == null || !App.mClementineConnection.isConnected()) { Log.d(TAG, "onDestroy - disconnect"); if (mOpenConnectDialog) { Intent connectDialog = new Intent(this, ConnectDialog.class); connectDialog.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(connectDialog); } } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); mFragments.get(mCurrentFragment).onActivityResult(requestCode, resultCode, data); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { int currentVolume = App.mClementine.getVolume(); // Control the volume of clementine if enabled in the options if (mSharedPref.getBoolean(App.SP_KEY_USE_VOLUMEKEYS, true)) { int volumeInc = Integer .parseInt(mSharedPref.getString(App.SP_VOLUME_INC, Clementine.DefaultVolumeInc)); switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: Message msgDown = Message.obtain(); msgDown.obj = ClementineMessageFactory .buildVolumeMessage(App.mClementine.getVolume() - volumeInc); App.mClementineConnection.mHandler.sendMessage(msgDown); if (currentVolume >= volumeInc) { currentVolume -= volumeInc; } else { currentVolume = 0; } makeToast(getString(R.string.playler_volume) + " " + currentVolume + "%", Toast.LENGTH_SHORT); return true; case KeyEvent.KEYCODE_VOLUME_UP: Message msgUp = Message.obtain(); msgUp.obj = ClementineMessageFactory .buildVolumeMessage(App.mClementine.getVolume() + volumeInc); App.mClementineConnection.mHandler.sendMessage(msgUp); if ((currentVolume + volumeInc) >= 100) { currentVolume = 100; } else { currentVolume += volumeInc; } makeToast(getString(R.string.playler_volume) + " " + currentVolume + "%", Toast.LENGTH_SHORT); return true; default: break; } } } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent keyEvent) { if (mSharedPref.getBoolean(App.SP_KEY_USE_VOLUMEKEYS, true)) { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { return true; } } return super.onKeyUp(keyCode, keyEvent); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { if (mDrawerLayout.isDrawerOpen(mDrawerList)) { mDrawerLayout.closeDrawer(mDrawerList); } else { mDrawerLayout.openDrawer(mDrawerList); } } return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { // Let the fragment handle the back button first if (mFragments.get(mCurrentFragment) == null || !mFragments.get(mCurrentFragment).isVisible() || !mFragments.get(mCurrentFragment).onBackPressed()) { super.onBackPressed(); } } /** * Request a disconnect from clementine */ private void requestDisconnect() { // Move the request to the message Message msg = Message.obtain(); msg.obj = ClementineMessage.getMessage(MsgType.DISCONNECT); // Send the request to the thread App.mClementineConnection.mHandler.sendMessage(msg); } /** * Disconnect was finished, now finish this activity */ void disconnect() { makeToast(R.string.player_disconnected, Toast.LENGTH_SHORT); if (mOpenConnectDialog) { setResult(ConnectDialog.RESULT_DISCONNECT); } else { setResult(ConnectDialog.RESULT_QUIT); } mLastPosition = 1; finish(); } /** * We got a message from Clementine. Here we process it for the main activity * and pass the data to the currently active fragment. * Info: Errormessages were already parsed in PlayerHandler! * * @param clementineMessage The message from Clementine */ void MessageFromClementine(ClementineMessage clementineMessage) { // Update the Player Fragment if (mFragments.get(mCurrentFragment) != null && mFragments.get(mCurrentFragment).isVisible() && mFragments.get(mCurrentFragment).isAdded()) { mFragments.get(mCurrentFragment).MessageFromClementine(clementineMessage); } if (mPlayerFragment != null) { mPlayerFragment.MessageFromClementine(clementineMessage); } } /** * Show text in a toast. Cancels previous toast * * @param resId The resource id * @param length length */ private void makeToast(int resId, int length) { makeToast(getString(resId), length); } /** * Show text in a toast. Cancels previous toast * * @param text The text to show * @param length length */ private void makeToast(String text, int length) { if (mToast != null) { mToast.cancel(); } mToast = Toast.makeText(this, text, length); mToast.show(); } private class DrawerItemClickListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (position != mLastPosition) { selectItem(position, 300); } } } /** * Swaps fragments in the main content view */ private void selectItem(final int position, int delay) { mDrawerLayout.closeDrawer(mDrawerList); new Handler().postDelayed(new Runnable() { @Override public void run() { if (mInstanceSaved) { return; } FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.setCustomAnimations(R.drawable.anim_fade_in, R.drawable.anim_fade_out); switch (position) { case 0: // Header Remote break; case 1: // Player if (mPlayerFragment != null) { ft.replace(R.id.content_frame, mFragments.get(2)).commit(); } else { ft.replace(R.id.content_frame, mFragments.get(0)).commit(); } mCurrentFragment = 0; mLastPosition = position; break; case 2: // Playlist ft.replace(R.id.content_frame, mFragments.get(1)).commit(); mCurrentFragment = 1; mLastPosition = position; break; case 3: // Library ft.replace(R.id.content_frame, mFragments.get(2)).commit(); mCurrentFragment = 2; mLastPosition = position; break; case 4: // Downloads ft.replace(R.id.content_frame, mFragments.get(3)).commit(); mCurrentFragment = 3; mLastPosition = position; break; case 5: // Header Settings break; case 6: // Settings Intent settingsIntent = new Intent(MainActivity.this, ClementineSettings.class); startActivity(settingsIntent); break; case 7: // Donate ft.replace(R.id.content_frame, mFragments.get(4)).commit(); mCurrentFragment = 4; mLastPosition = position; break; case 8: // Header Disconnect break; case 9: // Disonnect mOpenConnectDialog = true; requestDisconnect(); break; case 10: // Quit mOpenConnectDialog = false; requestDisconnect(); default: break; } } }, delay); } }