zoukankan      html  css  js  c++  java
  • 剖析Android自带Widget-Music播放器

    本文剖析Android自带widget-Music,其涉及到众多事件的处理,虽然麻烦,但可看出是如何和服务进行交互的。
    
    首先我们看下music程序中AndroidManifest.xml中有关widgets的定义。
    
    下面是xml/appwidget_info的内容,里面包含了这个widget程序的基本定义。
    
    android:minWidth="294dip" //最小宽度
    android:minHeight="72dip" //最小高度
    android:updatePeriodMillis="0" //更新频率
    android:initialLayout="@layout/album_appwidget"> //widget界面布局文件
    
    整个设计还是十分清晰,这里我们就不再做过多的赘述。
    
    public class MediaAppWidgetProvider extends AppWidgetProvider {
    
    public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate";
    
    static final ComponentName THIS_APPWIDGET =
    new ComponentName("com.android.music", "com.android.music.MediaAppWidgetProvider");
    
    private static MediaAppWidgetProvider sInstance;
    
    static synchronized MediaAppWidgetProvider getInstance() {
    if (sInstance == null) {
    sInstance = new MediaAppWidgetProvider();
    }
    return sInstance;
    }
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    defaultAppWidget(context, appWidgetIds);
    
    // 发送一个Intent广播给MediaPlaybackService以便立即更新
    
    Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD);
    updateIntent.putExtra(MediaPlaybackService.CMDNAME,MediaAppWidgetProvider.CMDAPPWIDGETUPDATE);
    updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
    updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    context.sendBroadcast(updateIntent);
    }
    
    /*
    * 初始化widget默认状态,如果服务没有运行,我们启动music的时候默认单击隐藏
    */
    private void defaultAppWidget(Context context, int[] appWidgetIds) {
    final Resources res = context.getResources();
    final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.album_appwidget);
    
    views.setViewVisibility(R.id.title, View.GONE);
    views.setTextViewText(R.id.artist, res.getText(R.string.emptyplaylist));
    
    linkButtons(context, views, false /* 没有播放*/);
    pushUpdate(context, appWidgetIds, views);
    }
    
    private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
    // 更新指定的列表
    
    final AppWidgetManager gm = AppWidgetManager.getInstance(context);
    if (appWidgetIds != null) {
    gm.updateAppWidget(appWidgetIds, views);
    } else {
    gm.updateAppWidget(THIS_APPWIDGET, views);
    }
    }
    
    /**
    * Check against {@link AppWidgetManager} if there are any instances of this widget.
    */
    private boolean hasInstances(Context context) {
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    int[] appWidgetIds = appWidgetManager.getAppWidgetIds(THIS_APPWIDGET);
    return (appWidgetIds.length > 0);
    }
    
    /**
    * Handle a change notification coming over from {@link MediaPlaybackService}
    */
    void notifyChange(MediaPlaybackService service, String what) {
    if (hasInstances(service)) {
    if (MediaPlaybackService.PLAYBACK_COMPLETE.equals(what) ||
    MediaPlaybackService.META_CHANGED.equals(what) ||
    MediaPlaybackService.PLAYSTATE_CHANGED.equals(what)) {
    performUpdate(service, null);
    }
    }
    }
    
    /**
    * Update all active widget instances by pushing changes
    */
    void performUpdate(MediaPlaybackService service, int[] appWidgetIds) {
    final Resources res = service.getResources();
    final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.album_appwidget);
    
    final int track = service.getQueuePosition() + 1;
    CharSequence titleName = service.getTrackName();
    CharSequence artistName = service.getArtistName();
    CharSequence errorState = null;
    
    // Format title string with track number, or show SD card message
    String status = Environment.getExternalStorageState();
    if (status.equals(Environment.MEDIA_SHARED) ||
    status.equals(Environment.MEDIA_UNMOUNTED)) {
    errorState = res.getText(R.string.sdcard_busy_title);
    } else if (status.equals(Environment.MEDIA_REMOVED)) {
    errorState = res.getText(R.string.sdcard_missing_title);
    } else if (titleName == null) {
    errorState = res.getText(R.string.emptyplaylist);
    }
    
    if (errorState != null) {
    // Show error state to user
    views.setViewVisibility(R.id.title, View.GONE);
    views.setTextViewText(R.id.artist, errorState);
    
    } else {
    // No error, so show normal titles
    views.setViewVisibility(R.id.title, View.VISIBLE);
    views.setTextViewText(R.id.title, titleName);
    views.setTextViewText(R.id.artist, artistName);
    }
    
    // Set correct drawable for pause state
    final boolean playing = service.isPlaying();
    if (playing) {
    views.setImageViewResource(R.id.control_play, R.drawable.appwidget_pause);
    } else {
    views.setImageViewResource(R.id.control_play, R.drawable.appwidget_play);
    }
    
    // Link actions buttons to intents
    linkButtons(service, views, playing);
    
    pushUpdate(service, appWidgetIds, views);
    }
    
    /**
    * Link up various button actions using {@link PendingIntents}.
    *
    * @param playerActive True if player is active in background, which means
    * widget click will launch {@link MediaPlaybackActivity},
    * otherwise we launch {@link MusicBrowserActivity}.
    */
    private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
    // Connect up various buttons and touch events
    Intent intent;
    PendingIntent pendingIntent;
    
    final ComponentName serviceName = new ComponentName(context, MediaPlaybackService.class);
    
    if (playerActive) {
    intent = new Intent(context, MediaPlaybackActivity.class);
    pendingIntent = PendingIntent.getActivity(context,
    0 /* no requestCode */, intent, 0 /* no flags */);
    views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
    } else {
    intent = new Intent(context, MusicBrowserActivity.class);
    pendingIntent = PendingIntent.getActivity(context,
    0 /* no requestCode */, intent, 0 /* no flags */);
    views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
    }
    
    intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION);
    intent.setComponent(serviceName);
    pendingIntent = PendingIntent.getService(context,
    0 /* no requestCode */, intent, 0 /* no flags */);
    views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
    
    intent = new Intent(MediaPlaybackService.NEXT_ACTION);
    intent.setComponent(serviceName);
    pendingIntent = PendingIntent.getService(context,
    0 /* no requestCode */, intent, 0 /* no flags */);
    views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
    }
    }
  • 相关阅读:
    在winfrom下利用c#代码,实现kindEditor的JavaScript方法:editor.html(),实现上报窗体的自动提交。
    Alwayson辅助副本上如何创建同步账号
    AD重建DNS
    SQL 删除用户的时候,产生“数据库主体在该数据库中拥有架构,无法删除”的解决办法
    FSLOGIX 安装记录,组策略记录
    使用FSLOGIX 部署配置文件漫游,首次生成VHD ,注销后无法登录。The user profile failed to attach please contact support
    定制ESXi版本下载
    SQL Server 数据库添加AlwaysOn高可用自定义登陆用户的方法
    RabbitMQ删除队列不重启消费者,动态重启
    获取某一年的某一周的周一//周日的日期
  • 原文地址:https://www.cnblogs.com/chengliu/p/4130628.html
Copyright © 2011-2022 走看看