/* 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.annotation.SuppressLint; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; 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.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import java.util.LinkedList; import de.qspool.clementineremote.App; import de.qspool.clementineremote.R; import de.qspool.clementineremote.SharedPreferencesKeys; import de.qspool.clementineremote.backend.Clementine; import de.qspool.clementineremote.backend.mediasession.ClementineMediaSessionNotification; 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.NavigationDrawerListAdapter; import de.qspool.clementineremote.ui.fragments.DonateFragment; import de.qspool.clementineremote.ui.fragments.DownloadsFragment; import de.qspool.clementineremote.ui.fragments.GlobalSearchFragment; 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.ui.interfaces.BackPressHandleable; import de.qspool.clementineremote.ui.interfaces.RemoteDataReceiver; import de.qspool.clementineremote.ui.settings.ClementineSettings; import de.qspool.clementineremote.ui.widgets.SlidingTabLayout; import de.qspool.clementineremote.utils.Utilities; public class MainActivity extends AppCompatActivity { private final static String MENU_POSITION = "last_menu_position"; private final String TAG = ((Object) this).getClass().getSimpleName(); private SharedPreferences mSharedPref; private MainActivityHandler mHandler; private Toast mToast; private int mCurrentFragment; private LinkedList<Fragment> mFragments = new LinkedList<>(); private Fragment mPlayerFragment; private RelativeLayout mDrawerMenu; private ListView mDrawerList; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private int mLastPosition = 1; private boolean mOpenConnectDialog = true; private boolean mInstanceSaved = false; @SuppressLint("SetTextI18n") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Keep screen on if user has requested this in preferences if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .getBoolean(SharedPreferencesKeys.SP_KEEP_SCREEN_ON, true) && Utilities.isRemoteConnected()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } setContentView(R.layout.activity_main); mSharedPref = PreferenceManager.getDefaultSharedPreferences(this); /* * Define here the available fragments in the main layout */ mFragments.add(new GlobalSearchFragment()); mFragments.add(new PlayerFragment()); mFragments.add(new PlaylistFragment()); mFragments.add(new LibraryFragment()); mFragments.add(new DownloadsFragment()); mFragments.add(new DonateFragment()); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); mDrawerMenu = (RelativeLayout) findViewById(R.id.drawer_menu_layout); mDrawerList = (ListView) findViewById(R.id.drawer_list); // Show Clementine with hostname and ip TextView clementineHost = (TextView) findViewById(R.id.drawer_menu_text1); TextView clementineIp = (TextView) findViewById(R.id.drawer_menu_text2); clementineHost.setText(String.format(getString(R.string.navigation_drawer_clementine_on), App.Clementine.getHostname())); clementineIp.setText(mSharedPref.getString(SharedPreferencesKeys.SP_KEY_IP, "") + ":" + mSharedPref .getString(SharedPreferencesKeys.SP_KEY_PORT, "")); if (findViewById(R.id.player_frame) != null) { mPlayerFragment = new PlayerFragment(); getFragmentManager().beginTransaction().add(R.id.player_frame, mPlayerFragment) .commit(); mLastPosition = 2; } // Create the header adapter LinkedList<NavigationDrawerListAdapter.NavigationDrawerItem> drawerItems = new LinkedList<>(); String[] itemNames = getResources().getStringArray(R.array.navigation_drawer_items); TypedArray itemIcons = getResources().obtainTypedArray(R.array.navigation_drawer_icons); for (int i=0;i<itemNames.length;i++) { String item = itemNames[i]; Drawable icon; try { icon = itemIcons.getDrawable(i); } catch (Resources.NotFoundException e) { icon = null; } NavigationDrawerListAdapter.NavigationDrawerItem.Type t = item.isEmpty() ? NavigationDrawerListAdapter.NavigationDrawerItem.Type.TYPE_SECTION : NavigationDrawerListAdapter.NavigationDrawerItem.Type.TYPE_ITEM; drawerItems.add(new NavigationDrawerListAdapter.NavigationDrawerItem(item, icon, t)); } itemIcons.recycle(); NavigationDrawerListAdapter navigationDrawerListAdapter = new NavigationDrawerListAdapter(this, R.layout.item_drawer_list, drawerItems); mDrawerList.setAdapter(navigationDrawerListAdapter); mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); mDrawerList.setDivider(null); mDrawerList.setDividerHeight(0); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle( this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.string.connectdialog_connect, /* "open drawer" description */ R.string.dialog_close /* "close drawer" description */ ) { }; // Set the drawer toggle as the DrawerListener mDrawerLayout.addDrawerListener(mDrawerToggle); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); // When we have a download notifitication and it was clicked, show the download. if (getIntent().hasExtra(ClementineMediaSessionNotification.EXTRA_NOTIFICATION_ID)) { int id = getIntent() .getIntExtra(ClementineMediaSessionNotification.EXTRA_NOTIFICATION_ID, 0); if (id == -1) { mLastPosition = 1; } else { mLastPosition = 4; } } selectItem(mLastPosition, 1); // Hide the tabs by default. It's the fragments responsibility to enable and disable them. SlidingTabLayout tabs = (SlidingTabLayout) findViewById(R.id.tabs); tabs.setVisibility(View.GONE); } @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); } } @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, 1); } @Override public void onResume() { super.onResume(); // Check if the user has changed the preferences to keep the screen on if (PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .getBoolean(SharedPreferencesKeys.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.ClementineConnection == null || App.Clementine == null || !App.ClementineConnection.isConnected()) { Log.d(TAG, "onResume - disconnect"); setResult(ConnectActivity.RESULT_DISCONNECT); finish(); } else { Log.d(TAG, "onResume - start"); // Set the handler mHandler = new MainActivityHandler(this); App.ClementineConnection.setUiHandler(mHandler); mDrawerList .performItemClick(mDrawerList.getAdapter().getView(mLastPosition, null, null), mLastPosition, mDrawerList.getAdapter().getItemId(mLastPosition)); } } @Override public void onPause() { super.onPause(); mHandler = null; if (App.ClementineConnection != null) { App.ClementineConnection.setUiHandler(null); } } @Override public void onDestroy() { super.onDestroy(); // If we disconnected, open connectdialog if (App.ClementineConnection == null || App.Clementine == null || !App.ClementineConnection.isConnected()) { Log.d(TAG, "onDestroy - disconnect"); if (mOpenConnectDialog) { Intent connectDialog = new Intent(this, ConnectActivity.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.Clementine.getVolume(); // Control the volume of clementine if enabled in the options if (mSharedPref.getBoolean(SharedPreferencesKeys.SP_KEY_USE_VOLUMEKEYS, true)) { int volumeInc = Integer.parseInt( mSharedPref.getString(SharedPreferencesKeys.SP_VOLUME_INC, Clementine.DefaultVolumeInc)); switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: Message msgDown = Message.obtain(); msgDown.obj = ClementineMessageFactory .buildVolumeMessage(App.Clementine.getVolume() - volumeInc); App.ClementineConnection.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.Clementine.getVolume() + volumeInc); App.ClementineConnection.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(SharedPreferencesKeys.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(mDrawerMenu)) { mDrawerLayout.closeDrawer(mDrawerMenu); } else { mDrawerLayout.openDrawer(mDrawerMenu); } } 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()) { if (!((BackPressHandleable) mFragments.get(mCurrentFragment)).onBackPressed()) super.onBackPressed(); } else { 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.ClementineConnection.mHandler.sendMessage(msg); } /** * Disconnect was finished, now finish this activity */ void disconnect() { makeToast(R.string.player_disconnected, Toast.LENGTH_SHORT); if (mOpenConnectDialog) { setResult(ConnectActivity.RESULT_DISCONNECT); } else { setResult(ConnectActivity.RESULT_QUIT); } mLastPosition = 0; 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()) { ((RemoteDataReceiver)mFragments.get(mCurrentFragment)).MessageFromClementine(clementineMessage); } if (mPlayerFragment != null) { ((RemoteDataReceiver)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(mDrawerMenu); new Handler().postDelayed(new Runnable() { @Override public void run() { if (mInstanceSaved) { return; } FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction ft = fragmentManager.beginTransaction(); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.setCustomAnimations(R.animator.anim_fade_in, R.animator.anim_fade_out); switch (position) { case 0: // Global search ft.replace(R.id.content_frame, mFragments.get(0)).commit(); mCurrentFragment = 0; mLastPosition = position; 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(1)).commit(); } mCurrentFragment = 1; mLastPosition = position; break; case 2: // Playlist ft.replace(R.id.content_frame, mFragments.get(2)).commit(); mCurrentFragment = 2; mLastPosition = position; break; case 3: // Library ft.replace(R.id.content_frame, mFragments.get(3)).commit(); mCurrentFragment = 3; mLastPosition = position; break; case 4: // Downloads ft.replace(R.id.content_frame, mFragments.get(4)).commit(); mCurrentFragment = 4; 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(5)).commit(); mCurrentFragment = 5; mLastPosition = position; break; case 8: // Header Disconnect break; case 9: // Quit mOpenConnectDialog = false; requestDisconnect(); default: break; } } }, delay); } }