1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.music; 18 19 import android.app.Activity; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.content.SharedPreferences.Editor; 25 import android.content.res.Resources; 26 import android.database.Cursor; 27 import android.graphics.Bitmap; 28 import android.graphics.Canvas; 29 import android.graphics.ColorFilter; 30 import android.graphics.PixelFormat; 31 import android.graphics.drawable.BitmapDrawable; 32 import android.graphics.drawable.Drawable; 33 import android.media.MediaMetadata; 34 import android.media.session.MediaController; 35 import android.net.Uri; 36 import android.provider.MediaStore; 37 import android.view.View; 38 import android.widget.TabWidget; 39 import android.widget.TextView; 40 import com.android.music.utils.LogHelper; 41 import com.android.music.utils.MusicProvider; 42 43 import java.util.Formatter; 44 import java.util.Iterator; 45 import java.util.Locale; 46 47 /* 48 Static methods useful for activities 49 */ 50 public class MusicUtils { 51 private static final String TAG = LogHelper.makeLogTag(MusicUtils.class); 52 53 public static final String TAG_MEDIA_ID = "__MEDIA_ID"; 54 public static final String TAG_PARENT_ITEM = "__PARENT_ITEM"; 55 public static final String TAG_WITH_TABS = "__WITH_TABS"; 56 57 // A really simple BitmapDrawable-like class, that doesn't do 58 // scaling, dithering or filtering. 59 private static class FastBitmapDrawable extends Drawable { 60 private Bitmap mBitmap; 61 public FastBitmapDrawable(Bitmap b) { 62 mBitmap = b; 63 } 64 @Override 65 public void draw(Canvas canvas) { 66 canvas.drawBitmap(mBitmap, 0, 0, null); 67 } 68 @Override 69 public int getOpacity() { 70 return PixelFormat.OPAQUE; 71 } 72 @Override 73 public void setAlpha(int alpha) {} 74 @Override 75 public void setColorFilter(ColorFilter cf) {} 76 } 77 78 public static Bitmap resizeBitmap(Bitmap bitmap, Bitmap ref) { 79 int w = ref.getWidth(); 80 int h = ref.getHeight(); 81 return Bitmap.createScaledBitmap(bitmap, w, h, false); 82 } 83 84 public static Drawable getDrawableBitmap(Bitmap bitmap, BitmapDrawable defaultArtwork) { 85 final Bitmap icon = defaultArtwork.getBitmap(); 86 int w = icon.getWidth(); 87 int h = icon.getHeight(); 88 bitmap = Bitmap.createScaledBitmap(bitmap, w, h, false); 89 return new FastBitmapDrawable(bitmap); 90 } 91 92 public static String makeAlbumsLabel( 93 Context context, int numalbums, int numsongs, boolean isUnknown) { 94 // There are two formats for the albums/songs information: 95 // "N Song(s)" - used for unknown artist/album 96 // "N Album(s)" - used for known albums 97 98 StringBuilder songs_albums = new StringBuilder(); 99 100 Resources r = context.getResources(); 101 if (isUnknown) { 102 if (numsongs == 1) { 103 songs_albums.append(context.getString(R.string.onesong)); 104 } else { 105 String f = r.getQuantityText(R.plurals.Nsongs, numsongs).toString(); 106 sFormatBuilder.setLength(0); 107 sFormatter.format(f, Integer.valueOf(numsongs)); 108 songs_albums.append(sFormatBuilder); 109 } 110 } else { 111 String f = r.getQuantityText(R.plurals.Nalbums, numalbums).toString(); 112 sFormatBuilder.setLength(0); 113 sFormatter.format(f, Integer.valueOf(numalbums)); 114 songs_albums.append(sFormatBuilder); 115 songs_albums.append(context.getString(R.string.albumsongseparator)); 116 } 117 return songs_albums.toString(); 118 } 119 120 /** 121 * This is now only used for the query screen 122 */ 123 public static String makeAlbumsSongsLabel( 124 Context context, int numalbums, int numsongs, boolean isUnknown) { 125 // There are several formats for the albums/songs information: 126 // "1 Song" - used if there is only 1 song 127 // "N Songs" - used for the "unknown artist" item 128 // "1 Album"/"N Songs" 129 // "N Album"/"M Songs" 130 // Depending on locale, these may need to be further subdivided 131 132 StringBuilder songs_albums = new StringBuilder(); 133 134 if (numsongs == 1) { 135 songs_albums.append(context.getString(R.string.onesong)); 136 } else { 137 Resources r = context.getResources(); 138 if (!isUnknown) { 139 String f = r.getQuantityText(R.plurals.Nalbums, numalbums).toString(); 140 sFormatBuilder.setLength(0); 141 sFormatter.format(f, Integer.valueOf(numalbums)); 142 songs_albums.append(sFormatBuilder); 143 songs_albums.append(context.getString(R.string.albumsongseparator)); 144 } 145 String f = r.getQuantityText(R.plurals.Nsongs, numsongs).toString(); 146 sFormatBuilder.setLength(0); 147 sFormatter.format(f, Integer.valueOf(numsongs)); 148 songs_albums.append(sFormatBuilder); 149 } 150 return songs_albums.toString(); 151 } 152 153 /* Try to use String.format() as little as possible, because it creates a 154 * new Formatter every time you call it, which is very inefficient. 155 * Reusing an existing Formatter more than tripled the speed of 156 * makeTimeString(). 157 * This Formatter/StringBuilder are also used by makeAlbumSongsLabel() 158 */ 159 private static StringBuilder sFormatBuilder = new StringBuilder(); 160 private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault()); 161 private static final Object[] sTimeArgs = new Object[5]; 162 163 public static String makeTimeString(Context context, long secs) { 164 String durationformat = context.getString( 165 secs < 3600 ? R.string.durationformatshort : R.string.durationformatlong); 166 167 /* Provide multiple arguments so the format can be changed easily 168 * by modifying the xml. 169 */ 170 sFormatBuilder.setLength(0); 171 172 final Object[] timeArgs = sTimeArgs; 173 timeArgs[0] = secs / 3600; 174 timeArgs[1] = secs / 60; 175 timeArgs[2] = (secs / 60) % 60; 176 timeArgs[3] = secs; 177 timeArgs[4] = secs % 60; 178 179 return sFormatter.format(durationformat, timeArgs).toString(); 180 } 181 182 static int getIntPref(Context context, String name, int def) { 183 SharedPreferences prefs = 184 context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); 185 return prefs.getInt(name, def); 186 } 187 188 static void setIntPref(Context context, String name, int value) { 189 SharedPreferences prefs = 190 context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); 191 Editor ed = prefs.edit(); 192 ed.putInt(name, value); 193 SharedPreferencesCompat.apply(ed); 194 } 195 196 static int sActiveTabIndex = -1; 197 198 static boolean updateButtonBar(Activity a, int highlight) { 199 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar); 200 boolean withtabs = false; 201 Intent intent = a.getIntent(); 202 if (intent != null) { 203 withtabs = intent.getBooleanExtra(MusicUtils.TAG_WITH_TABS, false); 204 } 205 206 if (highlight == 0 || !withtabs) { 207 ll.setVisibility(View.GONE); 208 return withtabs; 209 } else if (withtabs) { 210 ll.setVisibility(View.VISIBLE); 211 } 212 for (int i = ll.getChildCount() - 1; i >= 0; i--) { 213 View v = ll.getChildAt(i); 214 boolean isActive = (v.getId() == highlight); 215 if (isActive) { 216 ll.setCurrentTab(i); 217 sActiveTabIndex = i; 218 } 219 v.setTag(i); 220 v.setOnFocusChangeListener(new View.OnFocusChangeListener() { 221 222 public void onFocusChange(View v, boolean hasFocus) { 223 if (hasFocus) { 224 for (int i = 0; i < ll.getTabCount(); i++) { 225 if (ll.getChildTabViewAt(i) == v) { 226 ll.setCurrentTab(i); 227 processTabClick((Activity) ll.getContext(), v, 228 ll.getChildAt(sActiveTabIndex).getId()); 229 break; 230 } 231 } 232 } 233 } 234 }); 235 236 v.setOnClickListener(new View.OnClickListener() { 237 238 public void onClick(View v) { 239 processTabClick( 240 (Activity) ll.getContext(), v, ll.getChildAt(sActiveTabIndex).getId()); 241 } 242 }); 243 } 244 return withtabs; 245 } 246 247 static void processTabClick(Activity a, View v, int current) { 248 int id = v.getId(); 249 if (id == current) { 250 return; 251 } 252 253 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar); 254 255 activateTab(a, id); 256 if (id != R.id.nowplayingtab) { 257 ll.setCurrentTab((Integer) v.getTag()); 258 setIntPref(a, "activetab", id); 259 } 260 } 261 262 static void activateTab(Activity a, int id) { 263 Intent intent = new Intent(Intent.ACTION_PICK); 264 switch (id) { 265 case R.id.artisttab: 266 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/artistalbum"); 267 break; 268 case R.id.albumtab: 269 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/album"); 270 break; 271 case R.id.songtab: 272 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track"); 273 break; 274 case R.id.playlisttab: 275 intent.setDataAndType(Uri.EMPTY, MediaStore.Audio.Playlists.CONTENT_TYPE); 276 break; 277 case R.id.nowplayingtab: 278 intent = new Intent(a, MediaPlaybackActivity.class); 279 a.startActivity(intent); 280 // fall through and return 281 default: 282 return; 283 } 284 intent.putExtra(MusicUtils.TAG_WITH_TABS, true); 285 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 286 a.startActivity(intent); 287 a.finish(); 288 a.overridePendingTransition(0, 0); 289 } 290 291 static void updateNowPlaying(Activity a) { 292 View nowPlayingView = a.findViewById(R.id.nowplaying); 293 if (nowPlayingView == null) { 294 return; 295 } 296 MediaController controller = a.getMediaController(); 297 if (controller != null) { 298 MediaMetadata metadata = controller.getMetadata(); 299 if (metadata != null) { 300 TextView title = (TextView) nowPlayingView.findViewById(R.id.title); 301 TextView artist = (TextView) nowPlayingView.findViewById(R.id.artist); 302 title.setText(metadata.getString(MediaMetadata.METADATA_KEY_TITLE)); 303 String artistName = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST); 304 if (MusicProvider.UNKOWN.equals(artistName)) { 305 artistName = a.getString(R.string.unknown_artist_name); 306 } 307 artist.setText(artistName); 308 nowPlayingView.setVisibility(View.VISIBLE); 309 nowPlayingView.setOnClickListener(new View.OnClickListener() { 310 public void onClick(View v) { 311 Context c = v.getContext(); 312 c.startActivity(new Intent(c, MediaPlaybackActivity.class)); 313 } 314 }); 315 return; 316 } 317 } 318 nowPlayingView.setVisibility(View.GONE); 319 } 320 } 321