blob: a8a93e15737b676ed2ce99c20632a0da68095181 [file] [log] [blame]
The Android Open Source Project792a2202009-03-03 19:32:30 -08001/*
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
17package com.android.music;
18
The Android Open Source Project792a2202009-03-03 19:32:30 -080019import android.app.Activity;
The Android Open Source Project792a2202009-03-03 19:32:30 -080020import android.content.ContentResolver;
The Android Open Source Project792a2202009-03-03 19:32:30 -080021import android.content.Context;
22import android.content.Intent;
The Android Open Source Project792a2202009-03-03 19:32:30 -080023import android.content.SharedPreferences;
24import android.content.SharedPreferences.Editor;
25import android.content.res.Resources;
26import android.database.Cursor;
27import android.graphics.Bitmap;
The Android Open Source Project792a2202009-03-03 19:32:30 -080028import android.graphics.Canvas;
29import android.graphics.ColorFilter;
30import android.graphics.PixelFormat;
31import android.graphics.drawable.BitmapDrawable;
32import android.graphics.drawable.Drawable;
Jack Hef02d3c62017-02-21 00:39:22 -050033import android.media.MediaMetadata;
34import android.media.session.MediaController;
The Android Open Source Project792a2202009-03-03 19:32:30 -080035import android.net.Uri;
The Android Open Source Project792a2202009-03-03 19:32:30 -080036import android.provider.MediaStore;
The Android Open Source Project792a2202009-03-03 19:32:30 -080037import android.view.View;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080038import android.widget.TabWidget;
The Android Open Source Project792a2202009-03-03 19:32:30 -080039import android.widget.TextView;
Jack Hef02d3c62017-02-21 00:39:22 -050040import com.android.music.utils.LogHelper;
41import com.android.music.utils.MusicProvider;
The Android Open Source Project792a2202009-03-03 19:32:30 -080042
Marco Nelissen7a16cc72009-06-16 08:54:17 -070043import java.util.Formatter;
Jack Hef02d3c62017-02-21 00:39:22 -050044import java.util.Iterator;
Marco Nelissen7a16cc72009-06-16 08:54:17 -070045import java.util.Locale;
46
Jack Hef02d3c62017-02-21 00:39:22 -050047/*
48Static methods useful for activities
49 */
The Android Open Source Project792a2202009-03-03 19:32:30 -080050public class MusicUtils {
Jack Hef02d3c62017-02-21 00:39:22 -050051 private static final String TAG = LogHelper.makeLogTag(MusicUtils.class);
The Android Open Source Project792a2202009-03-03 19:32:30 -080052
Jack Hef02d3c62017-02-21 00:39:22 -050053 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);
The Android Open Source Project792a2202009-03-03 19:32:30 -080090 }
91
Jack Heb0fba8b2017-01-26 15:54:38 -080092 public static String makeAlbumsLabel(
93 Context context, int numalbums, int numsongs, boolean isUnknown) {
The Android Open Source Project792a2202009-03-03 19:32:30 -080094 // 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
Jack Heb0fba8b2017-01-26 15:54:38 -080097
The Android Open Source Project792a2202009-03-03 19:32:30 -080098 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 */
Jack Heb0fba8b2017-01-26 15:54:38 -0800123 public static String makeAlbumsSongsLabel(
124 Context context, int numalbums, int numsongs, boolean isUnknown) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800125 // 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
Jack Heb0fba8b2017-01-26 15:54:38 -0800128 // "1 Album"/"N Songs"
The Android Open Source Project792a2202009-03-03 19:32:30 -0800129 // "N Album"/"M Songs"
130 // Depending on locale, these may need to be further subdivided
Jack Heb0fba8b2017-01-26 15:54:38 -0800131
The Android Open Source Project792a2202009-03-03 19:32:30 -0800132 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();
Jack Heb0fba8b2017-01-26 15:54:38 -0800138 if (!isUnknown) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800139 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 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800152
The Android Open Source Project792a2202009-03-03 19:32:30 -0800153 /* 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) {
Marco Nelissen1e5a5672009-09-02 11:09:15 -0700164 String durationformat = context.getString(
165 secs < 3600 ? R.string.durationformatshort : R.string.durationformatlong);
Jack Heb0fba8b2017-01-26 15:54:38 -0800166
The Android Open Source Project792a2202009-03-03 19:32:30 -0800167 /* 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 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800181
The Android Open Source Project792a2202009-03-03 19:32:30 -0800182 static int getIntPref(Context context, String name, int def) {
183 SharedPreferences prefs =
Jack Heb0fba8b2017-01-26 15:54:38 -0800184 context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800185 return prefs.getInt(name, def);
186 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800187
The Android Open Source Project792a2202009-03-03 19:32:30 -0800188 static void setIntPref(Context context, String name, int value) {
189 SharedPreferences prefs =
Jack Heb0fba8b2017-01-26 15:54:38 -0800190 context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800191 Editor ed = prefs.edit();
192 ed.putInt(name, value);
Brad Fitzpatrick14c3cae2010-09-10 09:38:57 -0700193 SharedPreferencesCompat.apply(ed);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800194 }
195
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800196 static int sActiveTabIndex = -1;
Jack Heb0fba8b2017-01-26 15:54:38 -0800197
Marco Nelissen19cea9e2009-12-14 15:59:30 -0800198 static boolean updateButtonBar(Activity a, int highlight) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800199 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar);
200 boolean withtabs = false;
201 Intent intent = a.getIntent();
202 if (intent != null) {
Jack Hef02d3c62017-02-21 00:39:22 -0500203 withtabs = intent.getBooleanExtra(MusicUtils.TAG_WITH_TABS, false);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800204 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800205
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800206 if (highlight == 0 || !withtabs) {
207 ll.setVisibility(View.GONE);
Marco Nelissen19cea9e2009-12-14 15:59:30 -0800208 return withtabs;
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800209 } else if (withtabs) {
210 ll.setVisibility(View.VISIBLE);
211 }
212 for (int i = ll.getChildCount() - 1; i >= 0; i--) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800213 View v = ll.getChildAt(i);
214 boolean isActive = (v.getId() == highlight);
215 if (isActive) {
216 ll.setCurrentTab(i);
217 sActiveTabIndex = i;
218 }
Jeff Hamilton333bf192009-12-14 00:48:30 -0600219 v.setTag(i);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800220 v.setOnFocusChangeListener(new View.OnFocusChangeListener() {
221
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800222 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);
Jack Heb0fba8b2017-01-26 15:54:38 -0800227 processTabClick((Activity) ll.getContext(), v,
228 ll.getChildAt(sActiveTabIndex).getId());
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800229 break;
230 }
231 }
232 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800233 }
234 });
235
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800236 v.setOnClickListener(new View.OnClickListener() {
237
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800238 public void onClick(View v) {
Jack Heb0fba8b2017-01-26 15:54:38 -0800239 processTabClick(
240 (Activity) ll.getContext(), v, ll.getChildAt(sActiveTabIndex).getId());
241 }
242 });
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800243 }
Marco Nelissen19cea9e2009-12-14 15:59:30 -0800244 return withtabs;
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800245 }
246
247 static void processTabClick(Activity a, View v, int current) {
248 int id = v.getId();
249 if (id == current) {
250 return;
251 }
Jeff Hamilton333bf192009-12-14 00:48:30 -0600252
253 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar);
Jeff Hamilton333bf192009-12-14 00:48:30 -0600254
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800255 activateTab(a, id);
256 if (id != R.id.nowplayingtab) {
Marco Nelissen38bcd922010-03-19 13:00:37 -0700257 ll.setCurrentTab((Integer) v.getTag());
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800258 setIntPref(a, "activetab", id);
259 }
260 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800261
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800262 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);
Jack Heb0fba8b2017-01-26 15:54:38 -0800280 // fall through and return
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800281 default:
282 return;
283 }
Jack Hef02d3c62017-02-21 00:39:22 -0500284 intent.putExtra(MusicUtils.TAG_WITH_TABS, true);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800285 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
286 a.startActivity(intent);
287 a.finish();
288 a.overridePendingTransition(0, 0);
289 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800290
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800291 static void updateNowPlaying(Activity a) {
292 View nowPlayingView = a.findViewById(R.id.nowplaying);
293 if (nowPlayingView == null) {
294 return;
295 }
Jack Hef02d3c62017-02-21 00:39:22 -0500296 MediaController controller = a.getMediaController();
297 if (controller != null) {
298 MediaMetadata metadata = controller.getMetadata();
299 if (metadata != null) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800300 TextView title = (TextView) nowPlayingView.findViewById(R.id.title);
301 TextView artist = (TextView) nowPlayingView.findViewById(R.id.artist);
Jack Hef02d3c62017-02-21 00:39:22 -0500302 title.setText(metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
303 String artistName = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
304 if (MusicProvider.UNKOWN.equals(artistName)) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800305 artistName = a.getString(R.string.unknown_artist_name);
306 }
307 artist.setText(artistName);
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800308 nowPlayingView.setVisibility(View.VISIBLE);
309 nowPlayingView.setOnClickListener(new View.OnClickListener() {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800310 public void onClick(View v) {
311 Context c = v.getContext();
312 c.startActivity(new Intent(c, MediaPlaybackActivity.class));
Jack Heb0fba8b2017-01-26 15:54:38 -0800313 }
314 });
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800315 return;
316 }
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800317 }
318 nowPlayingView.setVisibility(View.GONE);
319 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800320}