Android Open Source - GlassTunes Remote Metadata Provider






From Project

Back to project page GlassTunes.

License

The source code is released under:

Apache License

If you think the Android project GlassTunes listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*  
 * Copyright (C) 2013 by Alexander Leontev
 *// www  .  j av a2 s . c om
 *   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.woodblockwithoutco.remotemetadataprovider.media;

import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;

import com.woodblockwithoutco.remotemetadataprovider.internal.MetadataUpdaterCallback;
import com.woodblockwithoutco.remotemetadataprovider.internal.RemoteControlDisplay;
import com.woodblockwithoutco.remotemetadataprovider.media.enums.MediaCommand;
import com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnArtworkChangeListener;
import com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnMetadataChangeListener;
import com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnPlaybackStateChangeListener;
import com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnRemoteControlFeaturesChangeListener;

public final class RemoteMetadataProvider {

  private static RemoteMetadataProvider INSTANCE;
  private static final String TAG = "RemoteMetadataProvider";
  private OnArtworkChangeListener mArtworkListener;
  private AudioManager mAudioManager;
  private PendingIntent mClientIntent;
  private Context mContext;
  private OnRemoteControlFeaturesChangeListener mFeaturesListener;
  private Handler mHandler;
  private boolean mIsLooperUsed = false;
  private Looper mLooper;
  private OnMetadataChangeListener mMetadataListener;
  private MetadataUpdaterCallback mMetadataUpdaterCallback;
  private OnPlaybackStateChangeListener mPlaystateListener;
  private RemoteControlDisplay mRemoteControlDisplay;
  private boolean mShouldUpdateHandler;

  /*
   * Constructor should be private as we don't want multiple instances.
   */
  private RemoteMetadataProvider(Context context) {
    mContext = context;
    if (mContext != null) {
      mAudioManager = (AudioManager) mContext
          .getSystemService(Context.AUDIO_SERVICE);
    }
  }

  /**
   * Returns instance of RemoteMetadataProvider
   * 
   * @param context
   *            Current application context. This is required to get instance
   *            of AudioManager.
   * @return Active instance of RemoteMetadataProvider.
   */
  public static synchronized RemoteMetadataProvider getInstance(
      Context context) {
    if (INSTANCE == null) {
      INSTANCE = new RemoteMetadataProvider(context);
    }
    return INSTANCE;
  }

  /**
   * Acquires remote media controls. This method MUST be called whenever your
   * View displaying metadata is shown or else you will not receive metadata
   * updates and probably you won't be able to send media commands.
   */
  public void acquireRemoteControls() {
    if (mAudioManager != null) {
      // if we don't have any RemoteControlDisplay or we have to update
      // our Handler
      if (mRemoteControlDisplay == null || mShouldUpdateHandler) {
        if (mMetadataUpdaterCallback == null) {
          mMetadataUpdaterCallback = new MetadataUpdaterCallback(
              INSTANCE);
        }
        // if we don't have any Handler or we should update it.
        if (mHandler == null || mShouldUpdateHandler) {
          if (mIsLooperUsed) {
            mHandler = new Handler(mLooper,
                mMetadataUpdaterCallback);
          } else {
            mHandler = new Handler(mMetadataUpdaterCallback);
            mLooper = null;
          }
        }
        mRemoteControlDisplay = new RemoteControlDisplay(mHandler);
        mShouldUpdateHandler = false;
      }
      // registering our RemoteControlDisplay
      mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
    } else {
      Log.w(TAG,
          "Failed to get instance of AudioManager while acquiring remote media controls");
    }
  }

  /**
   * Drops remote media controls. This method MUST be called whenever your
   * View displaying metadata is hidden or else you may block other
   * applications working with remote media controls.
   * 
   * @param destroyRemoteControls
   *            Set this to true if you have problems with displaying artwork.
   *            Otherwise use false.
   */
  public void dropRemoteControls(boolean destroyRemoteControls) {
    if (mAudioManager != null) {
      mAudioManager.unregisterRemoteControlDisplay(mRemoteControlDisplay);
      if (destroyRemoteControls) {
        mRemoteControlDisplay = null;
      }
      // remove all the messages from Handler
      mHandler.removeMessages(RemoteControlDisplay.MSG_SET_ARTWORK);
      mHandler.removeMessages(RemoteControlDisplay.MSG_SET_GENERATION_ID);
      mHandler.removeMessages(RemoteControlDisplay.MSG_SET_METADATA);
      mHandler.removeMessages(RemoteControlDisplay.MSG_SET_TRANSPORT_CONTROLS);
      mHandler.removeMessages(RemoteControlDisplay.MSG_UPDATE_STATE);
    } else {
      Log.w(TAG,
          "Failed to get instance of AudioManager while adropping remote media controls");
    }
  }

  /**
   * @return Intent for launching current player or null if there is no
   *         current client.
   * @throws NameNotFoundException
   *             This exception will be thrown in case the launch intent for
   *             current player can't be found.
   */
  public Intent getCurrentClientIntent() throws NameNotFoundException {
    if (mClientIntent == null) {
      return null;
    }
    return mContext.getPackageManager().getLaunchIntentForPackage(
        mClientIntent.getTargetPackage());
  }

  /**
   * Returns current client media events receiver in form of PendingIntent.
   * 
   * @return Current client media events receiver or null if there is no
   *         client.
   */
  public PendingIntent getCurrentClientPendingIntent() {
    return mClientIntent;
  }

  /**
   * Returns user-defined Looper.
   * 
   * @return user-defined Looper or null if default is used.
   */
  public Looper getLooper() {
    return mLooper;
  }

  /**
   * Returns the registered callback for artwork change event.
   * 
   * @return The callback or null if there is nothing registered.
   */
  public OnArtworkChangeListener getOnArtworkChangeListener() {
    return mArtworkListener;
  }

  /**
   * Returns the registered callback for metadata change event.
   * 
   * @return The callback or null if there is nothing registered.
   */
  public OnMetadataChangeListener getOnMetadataChangeListener() {
    return mMetadataListener;
  }

  /**
   * Returns the registered callback for playback state change event.
   * 
   * @return The callback or null if there is nothing registered.
   */
  public OnPlaybackStateChangeListener getOnPlaybackStateChangeListener() {
    return mPlaystateListener;
  }

  /**
   * Returns the registered callback for remote control features change event.
   * 
   * @return The callback or null if there is nothing registered.
   */
  public OnRemoteControlFeaturesChangeListener getOnRemoteControlFlagsChangeListener() {
    return mFeaturesListener;
  }

  /**
   * Check if remote media client is active(e.g. can receive media events and
   * provide metadata).
   * 
   * @return true if there is remote media client to send events to and false
   *         otherwise.
   */
  public boolean isClientActive() {
    return !(mClientIntent == null);
  }

  /**
   * Tells the RemoteMetadataProvider to stop using the looper. Note that
   * there will be no effect until you call
   * {@link RemoteMetadataProvider#acquireRemoteControls()}.
   */
  public void removeLooper() {
    mIsLooperUsed = false;
    mShouldUpdateHandler = true;
  }

  private void sendBroadcastButton(int keyCode) {
    if (mContext != null) {
      long eventtime = SystemClock.uptimeMillis();
      Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
      KeyEvent keyEvent = new KeyEvent(eventtime, eventtime,
          KeyEvent.ACTION_DOWN, keyCode, 0);
      keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
      mContext.sendOrderedBroadcast(keyIntent, null);
      keyEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_UP);
      keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
      mContext.sendOrderedBroadcast(keyIntent, null);
    }
  }

  /**
   * Sends input event with the specified keycode system-wide in form of
   * Broadcast. Use this method only in case there is no active clients and
   * you want to try to initialize one. If there is active client, use
   * {@link RemoteMetadataProvider#sendMediaCommand(int)} instead. Please note
   * that not all players support this.
   * 
   * @param keyCode
   *            Button keycode to send.
   */
  public void sendBroadcastMediaCommand(int keyCode) {
    sendBroadcastButton(keyCode);
  }

  /**
   * Sends input event with the specified MediaCommand system-wide in form of
   * Broadcast. Use this method only in case there is no active clients and
   * you want to try to initialize one. If there is active client, use
   * {@link RemoteMetadataProvider#sendMediaCommand(MediaCommand)} instead.
   * Please note that not all players support this.
   * 
   * @param command
   *            MediaCommand to send.
   */
  public void sendBroadcastMediaCommand(MediaCommand command) {
    switch (command) {
    case REWIND:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_REWIND);
      break;
    case PREVIOUS:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
      break;
    case PLAY:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_PLAY);
      break;
    case PAUSE:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_PAUSE);
      break;
    case PLAY_PAUSE:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
      break;
    case STOP:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_STOP);
      break;
    case NEXT:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_NEXT);
      break;
    case FAST_FORWARD:
      sendBroadcastButton(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
      break;
    }
  }

  /*
   * Should not be used by user. Sends media button click event.
   */
  private boolean sendButton(int keyCode) {
    KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    try {
      if (mClientIntent == null) {
        return false;
      }
      mClientIntent.send(mContext, 0, intent);
    } catch (CanceledException e) {
      // will silently fail with false return
      return false;
    }
    keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
    intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    try {
      if (mClientIntent == null) {
        return false;
      }
      mClientIntent.send(mContext, 0, intent);
    } catch (CanceledException e) {
      // will silently fail with false return
      return false;
    }
    return true;
  }

  /**
   * Sends input event with the specified keycode to current player.
   * 
   * @param keyCode
   *            Button keycode to send.
   * @return True if event was delivered to player, false otherwise.
   */
  public boolean sendMediaCommand(int keyCode) {
    if (mClientIntent != null) {
      return sendButton(keyCode);
    }
    // will silently fail with false return if client is missing
    return false;
  }

  /**
   * Sends input event with the specified MediaCommand to current player.
   * 
   * @param command
   *            MediaCommand enum with necessary action.
   * @return True if event was delivered to player, false otherwise.
   */
  public boolean sendMediaCommand(MediaCommand command) {
    if (mClientIntent != null) {
      switch (command) {
      case REWIND:
        return sendButton(KeyEvent.KEYCODE_MEDIA_REWIND);
      case PREVIOUS:
        return sendButton(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
      case PLAY:
        return sendButton(KeyEvent.KEYCODE_MEDIA_PLAY);
      case PAUSE:
        return sendButton(KeyEvent.KEYCODE_MEDIA_PAUSE);
      case PLAY_PAUSE:
        return sendButton(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
      case STOP:
        return sendButton(KeyEvent.KEYCODE_MEDIA_STOP);
      case NEXT:
        return sendButton(KeyEvent.KEYCODE_MEDIA_NEXT);
      case FAST_FORWARD:
        return sendButton(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
      }
    }
    // will silently fail with false return if client is missing
    return false;
  }

  /**
   * Sets current client PendingIntent. Should not be used manually unless you
   * sure you are holding the right PendingIntent to send events to.
   */
  public void setCurrentClientPendingIntent(PendingIntent pintent) {
    mClientIntent = pintent;
  }

  /**
   * Sets looper to be used to process messages. Please note that effect
   * 
   * @param looper
   *            Looper to be used
   */
  public void setLooper(Looper looper) {
    mIsLooperUsed = true;
    mLooper = looper;
    mShouldUpdateHandler = true;
  }

  /**
   * Register a callback to be invoked when artwork should be updated.
   * 
   * @param l
   *            The callback that will run.
   */
  public void setOnArtworkChangeListener(OnArtworkChangeListener l) {
    mArtworkListener = l;
  }

  /**
   * Register a callback to be invoked when metadata should be updated.
   * 
   * @param l
   *            The callback that will run.
   */
  public void setOnMetadataChangeListener(OnMetadataChangeListener l) {
    mMetadataListener = l;
  }

  /**
   * Register a callback to be invoked when playback state should be updated.
   * 
   * @param l
   *            The callback that will run.
   */
  public void setOnPlaybackStateChangeListener(OnPlaybackStateChangeListener l) {
    mPlaystateListener = l;
  }

  /**
   * Register a callback to be invoked when remote control features should be
   * updated.
   * 
   * @param l
   *            The callback that will run.
   */
  public void setOnRemoteControlFeaturesChangeListener(
      OnRemoteControlFeaturesChangeListener l) {
    mFeaturesListener = l;
  }
}




Java Source Code List

com.glasstunes.BaseContentCardActivity.java
com.glasstunes.BrowseActivity.java
com.glasstunes.ConfirmationActivity.java
com.glasstunes.ControlsActivity.java
com.glasstunes.Debug.java
com.glasstunes.DetailActivity.java
com.glasstunes.GlassTunesApp.java
com.glasstunes.HeaderActivity.java
com.glasstunes.LauncherActivity.java
com.glasstunes.NowPlayingActivity.java
com.glasstunes.cards.ActionCard.java
com.glasstunes.cards.BrowseCard.java
com.glasstunes.cards.ContentCard.java
com.glasstunes.cards.HeaderCard.java
com.glasstunes.cards.LauncherCard.java
com.glasstunes.cards.NowPlayingCard.java
com.glasstunes.cards.PausePlayCard.java
com.glasstunes.cards.SkipNextCard.java
com.glasstunes.cards.SkipPreviousCard.java
com.glasstunes.controller.LocalMusicController.java
com.glasstunes.controller.MusicController.java
com.glasstunes.screenslide.BasicCardFragment.java
com.glasstunes.screenslide.BasicCardPagerAdapter.java
com.glasstunes.screenslide.BounceBackViewPager.java
com.glasstunes.screenslide.CardFragment.java
com.glasstunes.screenslide.ContextWrapperEdgeEffect.java
com.glasstunes.screenslide.ScreenSlideActivity.java
com.glasstunes.screenslide.ScreenSlidePagerAdapter.java
com.glasstunes.view.RobotoTypefaces.java
com.glasstunes.view.SliderView.java
com.glasstunes.view.TypophileTextView.java
com.viewpagerindicator.CirclePageIndicator.java
com.viewpagerindicator.IconPageIndicator.java
com.viewpagerindicator.IconPagerAdapter.java
com.viewpagerindicator.IcsLinearLayout.java
com.viewpagerindicator.LinePageIndicator.java
com.viewpagerindicator.PageIndicator.java
com.viewpagerindicator.TabPageIndicator.java
com.viewpagerindicator.TitlePageIndicator.java
com.viewpagerindicator.UnderlinePageIndicator.java
com.woodblockwithoutco.remotemetadataprovider.internal.MetadataUpdaterCallback.java
com.woodblockwithoutco.remotemetadataprovider.internal.RemoteControlDisplay.java
com.woodblockwithoutco.remotemetadataprovider.media.RemoteMetadataProvider.java
com.woodblockwithoutco.remotemetadataprovider.media.enums.MediaCommand.java
com.woodblockwithoutco.remotemetadataprovider.media.enums.PlayState.java
com.woodblockwithoutco.remotemetadataprovider.media.enums.RemoteControlFeature.java
com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnArtworkChangeListener.java
com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnMetadataChangeListener.java
com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnPlaybackStateChangeListener.java
com.woodblockwithoutco.remotemetadataprovider.media.listeners.OnRemoteControlFeaturesChangeListener.java