diff --git a/src/org/kreed/vanilla/Cache.java b/src/org/kreed/vanilla/Cache.java index 423c3dcb..066340d0 100644 --- a/src/org/kreed/vanilla/Cache.java +++ b/src/org/kreed/vanilla/Cache.java @@ -36,12 +36,12 @@ public class Cache { /** * The keys contained in the cache, stored in the order of insertion. */ - private long[] mKeys; + private final long[] mKeys; /** * The values contained in the cache, stored in a location corresponding * to the keys in mKeys. */ - private Object[] mValues; + private final Object[] mValues; /** * Create a Cache. @@ -61,7 +61,7 @@ public class Cache { * * @return The number of items in the cache. */ - public int count() + private int count() { long[] keys = mKeys; int count = keys.length; diff --git a/src/org/kreed/vanilla/CoverBitmap.java b/src/org/kreed/vanilla/CoverBitmap.java index 63e3b790..a48de96e 100644 --- a/src/org/kreed/vanilla/CoverBitmap.java +++ b/src/org/kreed/vanilla/CoverBitmap.java @@ -295,8 +295,8 @@ public final class CoverBitmap { int boxWidth = Math.min(maxBoxWidth, textSize + Math.max(titleWidth, Math.max(artistWidth, albumWidth)) + padding * 3); int boxHeight = Math.min(maxBoxHeight, textSize * 3 + padding * 4); - int bitmapWidth = (int)(horizontal ? coverWidth + boxWidth : Math.max(coverWidth, boxWidth)); - int bitmapHeight = (int)(horizontal ? Math.max(coverHeight, boxHeight) : coverHeight + boxHeight); + int bitmapWidth = horizontal ? coverWidth + boxWidth : Math.max(coverWidth, boxWidth); + int bitmapHeight = horizontal ? Math.max(coverHeight, boxHeight) : coverHeight + boxHeight; if (bitmap != null) { if (bitmap.getHeight() != bitmapHeight || bitmap.getWidth() != bitmapWidth) { @@ -391,7 +391,7 @@ public final class CoverBitmap { * @param height Maximum height of image * @return The scaled Bitmap, or null if a cover could not be found. */ - public static Bitmap createScaledBitmap(Context context, Song song, int width, int height) + private static Bitmap createScaledBitmap(Context context, Song song, int width, int height) { if (song == null || width < 1 || height < 1) return null; diff --git a/src/org/kreed/vanilla/CoverView.java b/src/org/kreed/vanilla/CoverView.java index b9093f38..8b320cc0 100644 --- a/src/org/kreed/vanilla/CoverView.java +++ b/src/org/kreed/vanilla/CoverView.java @@ -66,17 +66,17 @@ public final class CoverView extends View implements Handler.Callback { /** * The current set of songs: 0 = previous, 1 = current, and 2 = next. */ - private Song[] mSongs = new Song[3]; + private final Song[] mSongs = new Song[3]; /** * The covers for the current songs: 0 = previous, 1 = current, and 2 = next. */ - private Bitmap[] mBitmaps = new Bitmap[3]; + private final Bitmap[] mBitmaps = new Bitmap[3]; /** * Cache of cover bitmaps generated for songs. The song ids are the keys. */ - private Cache mBitmapCache = new Cache(8); + private final Cache mBitmapCache = new Cache(8); - private Scroller mScroller; + private final Scroller mScroller; private VelocityTracker mVelocityTracker; private float mLastMotionX; private float mLastMotionY; @@ -358,7 +358,7 @@ public final class CoverView extends View implements Handler.Callback { } /** - * Call {@link CoverView#generateBitmap(Song)} for the given song. + * Call {@link CoverView#generateBitmap(int)} for the song at the given index. * * obj must be the Song to generate a bitmap for. */ diff --git a/src/org/kreed/vanilla/FullPlaybackActivity.java b/src/org/kreed/vanilla/FullPlaybackActivity.java index f1b6eb95..2b0479c4 100644 --- a/src/org/kreed/vanilla/FullPlaybackActivity.java +++ b/src/org/kreed/vanilla/FullPlaybackActivity.java @@ -50,7 +50,7 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On * A Handler running on the UI thread, in contrast with mHandler which runs * on a worker thread. */ - private Handler mUiHandler = new Handler(this); + private final Handler mUiHandler = new Handler(this); private TextView mOverlayText; private View mControlsTop; @@ -77,7 +77,7 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On /** * Cached StringBuilder for formatting track position. */ - private StringBuilder mTimeBuilder = new StringBuilder(); + private final StringBuilder mTimeBuilder = new StringBuilder(); @Override public void onCreate(Bundle icicle) @@ -89,7 +89,7 @@ public class FullPlaybackActivity extends PlaybackActivity implements SeekBar.On boolean hiddenControls = settings.getBoolean("hidden_controls", false); int layout = R.layout.full_playback; - int coverStyle = -1; + int coverStyle; switch (displayMode) { default: diff --git a/src/org/kreed/vanilla/IdlePreference.java b/src/org/kreed/vanilla/IdlePreference.java index 83df5f29..d8126f65 100644 --- a/src/org/kreed/vanilla/IdlePreference.java +++ b/src/org/kreed/vanilla/IdlePreference.java @@ -40,8 +40,8 @@ import android.widget.TextView; * (6 hours). The values range on an approximately exponential scale. */ public class IdlePreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener { - private static int MIN = 60; - private static int MAX = 21600; + private static final int MIN = 60; + private static final int MAX = 21600; /** * The current idle timeout displayed on the slider. Will not be persisted diff --git a/src/org/kreed/vanilla/LibraryActivity.java b/src/org/kreed/vanilla/LibraryActivity.java index 4cdce8fe..4be8d237 100644 --- a/src/org/kreed/vanilla/LibraryActivity.java +++ b/src/org/kreed/vanilla/LibraryActivity.java @@ -93,7 +93,7 @@ public class LibraryActivity extends PlaybackActivity implements AdapterView.OnI private MediaAdapter mGenreAdapter; private MediaAdapter mCurrentAdapter; - private ContentObserver mPlaylistObserver = new ContentObserver(null) { + private final ContentObserver mPlaylistObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { @@ -374,10 +374,9 @@ public class LibraryActivity extends PlaybackActivity implements AdapterView.OnI LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.leftMargin = 5; + PaintDrawable background = new PaintDrawable(Color.GRAY); + background.setCornerRadius(5); for (int i = 0; i != limiter.length; ++i) { - PaintDrawable background = new PaintDrawable(Color.GRAY); - background.setCornerRadius(5); - TextView view = new TextView(this); view.setSingleLine(); view.setEllipsize(TextUtils.TruncateAt.MARQUEE); @@ -687,11 +686,11 @@ public class LibraryActivity extends PlaybackActivity implements AdapterView.OnI * Called by MediaAdapters to requery their data on the worker thread. * obj will contain the MediaAdapter. */ - public static final int MSG_RUN_QUERY = 14; + private static final int MSG_RUN_QUERY = 14; /** * Call addToPlaylist with data from the intent in obj. */ - public static final int MSG_ADD_TO_PLAYLIST = 15; + private static final int MSG_ADD_TO_PLAYLIST = 15; @Override public boolean handleMessage(Message message) @@ -766,7 +765,7 @@ public class LibraryActivity extends PlaybackActivity implements AdapterView.OnI * * @param adapter The adapter to run the query for. */ - public void runQuery(MediaAdapter adapter) + private void runQuery(MediaAdapter adapter) { mHandler.removeMessages(MSG_RUN_QUERY, adapter); mHandler.sendMessage(mHandler.obtainMessage(MSG_RUN_QUERY, adapter)); diff --git a/src/org/kreed/vanilla/MediaAdapter.java b/src/org/kreed/vanilla/MediaAdapter.java index 95c3929a..58bebd5d 100644 --- a/src/org/kreed/vanilla/MediaAdapter.java +++ b/src/org/kreed/vanilla/MediaAdapter.java @@ -33,6 +33,7 @@ import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.SectionIndexer; import java.io.Serializable; +import java.util.regex.Pattern; /** * MediaAdapter provides an adapter backed by a MediaStore content provider. @@ -46,16 +47,18 @@ import java.io.Serializable; * See getLimiter and setLimiter for details. */ public class MediaAdapter extends CursorAdapter implements SectionIndexer { + private static final Pattern SPACE_SPLIT = Pattern.compile("\\s+"); + /** * A context to use. */ - private Context mContext; + private final Context mContext; /** * The type of media represented by this adapter. Must be one of the * MediaUtils.FIELD_* constants. Determines which content provider to query for * media and what fields to display. */ - private int mType; + private final int mType; /** * The URI of the content provider backing this adapter. */ @@ -78,7 +81,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { /** * If true, show an expand arrow next the the text in each view. */ - private boolean mExpandable; + private final boolean mExpandable; /** * A limiter is used for filtering. The intention is to restrict items * displayed in the list to only those of a specific artist or album, as @@ -92,12 +95,12 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { /** * The section indexer, for the letter pop-up when scrolling. */ - private MusicAlphabetIndexer mIndexer; + private final MusicAlphabetIndexer mIndexer; /** * True if this adapter should have a special MediaView with custom text in * the first row. */ - private boolean mHasHeader; + private final boolean mHasHeader; /** * The text to show in the header. */ @@ -290,7 +293,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { String[] selectionArgs = null; String sort; - if (mLimiter != null && mLimiter.type == MediaUtils.TYPE_ALBUM) + if (limiter != null && limiter.type == MediaUtils.TYPE_ALBUM) sort = MediaStore.Audio.Media.TRACK; else if (mFieldKeys == null) sort = mFields[mFields.length - 1]; @@ -302,6 +305,7 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { if (constraint != null && constraint.length() != 0) { String[] needles; + String[] keySource; // If we are using sorting keys, we need to change our constraint // into a list of collation keys. Otherwise, just split the @@ -310,17 +314,21 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { String colKey = MediaStore.Audio.keyFor(constraint); String spaceColKey = DatabaseUtils.getCollationKey(" "); needles = colKey.split(spaceColKey); + keySource = mFieldKeys; } else { - needles = constraint.split("\\s+"); + needles = SPACE_SPLIT.split(constraint); + keySource = mFields; } int size = needles.length; selectionArgs = new String[size]; - String[] keySource = mFieldKeys == null ? mFields : mFieldKeys; - String keys = keySource[0]; - for (int j = 1; j != keySource.length; ++j) - keys += "||" + keySource[j]; + StringBuilder keys = new StringBuilder(20); + keys.append(keySource[0]); + for (int j = 1; j != keySource.length; ++j) { + keys.append("||"); + keys.append(keySource[j]); + } for (int j = 0; j != needles.length; ++j) { selectionArgs[j] = '%' + needles[j] + '%'; @@ -338,12 +346,12 @@ public class MediaAdapter extends CursorAdapter implements SectionIndexer { if (limiter != null && limiter.type == MediaUtils.TYPE_GENRE) { // Genre is not standard metadata for MediaStore.Audio.Media. // We have to query it through a separate provider. : / - return MediaUtils.buildGenreQuery(mLimiter.id, projection, selection.toString(), selectionArgs); + return MediaUtils.buildGenreQuery(limiter.id, projection, selection.toString(), selectionArgs); } else { if (limiter != null) { if (selection.length() != 0) selection.append(" AND "); - selection.append(mLimiter.selection); + selection.append(limiter.selection); } return new QueryTask(mStore, projection, selection.toString(), selectionArgs, sort); diff --git a/src/org/kreed/vanilla/MediaButtonHandler.java b/src/org/kreed/vanilla/MediaButtonHandler.java index 4de22f6b..8bf468fa 100644 --- a/src/org/kreed/vanilla/MediaButtonHandler.java +++ b/src/org/kreed/vanilla/MediaButtonHandler.java @@ -56,7 +56,7 @@ public class MediaButtonHandler { */ private static int mUseControls = -1; - private Context mContext; + private final Context mContext; /** * Whether the phone is currently in a call. 1 for yes, 0 for no, -1 for * uninitialized. @@ -67,10 +67,10 @@ public class MediaButtonHandler { */ private long mLastClickTime; - private static AudioManager mAudioManager; + private static final AudioManager mAudioManager; private static Method mRegisterMediaButtonEventReceiver; private static Method mUnregisterMediaButtonEventReceiver; - public static ComponentName mButtonReceiver; + public static final ComponentName mButtonReceiver; /** * Retrieve the MediaButtonHandler singleton, creating it if necessary. diff --git a/src/org/kreed/vanilla/MediaUtils.java b/src/org/kreed/vanilla/MediaUtils.java index 593cb7e5..1f51eb79 100644 --- a/src/org/kreed/vanilla/MediaUtils.java +++ b/src/org/kreed/vanilla/MediaUtils.java @@ -75,7 +75,7 @@ public class MediaUtils { * Query this many songs at a time from sAllSongs. */ private static final int RANDOM_POPULATE_SIZE = 20; - private static Song[] sRandomCache = new Song[RANDOM_POPULATE_SIZE]; + private static final Song[] sRandomCache = new Song[RANDOM_POPULATE_SIZE]; private static int sRandomCacheIdx; private static int sRandomCacheEnd; diff --git a/src/org/kreed/vanilla/MediaView.java b/src/org/kreed/vanilla/MediaView.java index 69984d20..71e37d38 100644 --- a/src/org/kreed/vanilla/MediaView.java +++ b/src/org/kreed/vanilla/MediaView.java @@ -95,7 +95,7 @@ public final class MediaView extends View { /** * True if the expander should be shown. */ - private boolean mExpandable; + private final boolean mExpandable; /** * The cached measured view height. */ @@ -117,7 +117,7 @@ public final class MediaView extends View { mExpandable = expandable; - mViewHeight = (int)(7 * sTextSize / 2); + mViewHeight = 7 * sTextSize / 2; if (expandable) mViewHeight = Math.max(mViewHeight, sExpander.getHeight() + sTextSize); } diff --git a/src/org/kreed/vanilla/NewPlaylistDialog.java b/src/org/kreed/vanilla/NewPlaylistDialog.java index 22b62614..cf81ea9c 100644 --- a/src/org/kreed/vanilla/NewPlaylistDialog.java +++ b/src/org/kreed/vanilla/NewPlaylistDialog.java @@ -56,16 +56,16 @@ public class NewPlaylistDialog extends Dialog implements TextWatcher, View.OnCli * The text to display initially. When the EditText contains this text, the * positive button will be disabled. */ - private String mInitialText; + private final String mInitialText; /** * The resource containing the string describing the default positive * action (e.g. "Create" or "Rename"). */ - private int mActionRes; + private final int mActionRes; /** * An intent that is simply stored in the dialog. */ - private Intent mIntent; + private final Intent mIntent; /** * Create a NewPlaylistDialog. diff --git a/src/org/kreed/vanilla/PlaybackActivity.java b/src/org/kreed/vanilla/PlaybackActivity.java index 8af8bfd9..a35724fb 100644 --- a/src/org/kreed/vanilla/PlaybackActivity.java +++ b/src/org/kreed/vanilla/PlaybackActivity.java @@ -302,7 +302,6 @@ public class PlaybackActivity extends Activity { } - static final int MENU_DISPLAY = 1; static final int MENU_PREFS = 2; static final int MENU_LIBRARY = 3; static final int MENU_SHUFFLE = 4; diff --git a/src/org/kreed/vanilla/PlaybackService.java b/src/org/kreed/vanilla/PlaybackService.java index a2bc6762..1b81410f 100644 --- a/src/org/kreed/vanilla/PlaybackService.java +++ b/src/org/kreed/vanilla/PlaybackService.java @@ -164,9 +164,9 @@ public final class PlaybackService extends Service implements Handler.Callback, public static final int WHEN_PLAYING = 1; public static final int ALWAYS = 2; - private static Object sWait = new Object(); + private static final Object sWait = new Object(); private static PlaybackService sInstance; - private static ArrayList sActivities = new ArrayList(); + private static final ArrayList sActivities = new ArrayList(5); /** * Cached app-wide SharedPreferences instance. */ @@ -210,7 +210,7 @@ public final class PlaybackService extends Service implements Handler.Callback, int mState; private Song mCurrentSong; - Object mStateLock = new Object(); + private final Object mStateLock = new Object(); boolean mPlayingBeforeCall; private int mPendingSeek; public Receiver mReceiver; @@ -416,25 +416,21 @@ public final class PlaybackService extends Service implements Handler.Callback, /** * Set a state flag. - * - * @return The new state. */ - public int setFlag(int flag) + public void setFlag(int flag) { synchronized (mStateLock) { - return updateState(mState | flag); + updateState(mState | flag); } } /** * Unset a state flag. - * - * @return The new state. */ - public int unsetFlag(int flag) + public void unsetFlag(int flag) { synchronized (mStateLock) { - return updateState(mState & ~flag); + updateState(mState & ~flag); } } @@ -668,7 +664,7 @@ public final class PlaybackService extends Service implements Handler.Callback, else if ((state & FLAG_REPEAT) == 0) state |= FLAG_REPEAT; else if ((state & FLAG_REPEAT) != 0) - state = (state | FLAG_REPEAT_CURRENT) & ~FLAG_REPEAT;; + state = (state | FLAG_REPEAT_CURRENT) & ~FLAG_REPEAT; return updateState(state); } } @@ -752,7 +748,7 @@ public final class PlaybackService extends Service implements Handler.Callback, updateState(mState & ~FLAG_ERROR); } } catch (IOException e) { - mErrorMessage = getResources().getString(R.string.song_load_failed, song == null ? null : song.path); + mErrorMessage = getResources().getString(R.string.song_load_failed, song.path); updateState(mState | FLAG_ERROR); Toast.makeText(this, mErrorMessage, Toast.LENGTH_LONG).show(); Log.e("VanillaMusic", "IOException", e); @@ -777,7 +773,7 @@ public final class PlaybackService extends Service implements Handler.Callback, @Override public boolean onError(MediaPlayer player, int what, int extra) { - Log.e("VanillaMusic", "MediaPlayer error: " + what + " " + extra); + Log.e("VanillaMusic", "MediaPlayer error: " + what + ' ' + extra); return true; } @@ -828,7 +824,7 @@ public final class PlaybackService extends Service implements Handler.Callback, mPlugInitialized = true; } } - }; + } private class InCallListener extends PhoneStateListener { @Override @@ -862,7 +858,7 @@ public final class PlaybackService extends Service implements Handler.Callback, } } } - }; + } public void onMediaChange() { @@ -1206,7 +1202,7 @@ public final class PlaybackService extends Service implements Handler.Callback, mHandler.sendEmptyMessageDelayed(SAVE_STATE, 5000); } - private ContentObserver mObserver = new ContentObserver(null) { + private final ContentObserver mObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { @@ -1228,7 +1224,7 @@ public final class PlaybackService extends Service implements Handler.Callback, synchronized (sWait) { sWait.wait(); } - } catch (InterruptedException e) { + } catch (InterruptedException ignored) { } } } diff --git a/src/org/kreed/vanilla/Playlist.java b/src/org/kreed/vanilla/Playlist.java index 07bf39ec..76c9ecd7 100644 --- a/src/org/kreed/vanilla/Playlist.java +++ b/src/org/kreed/vanilla/Playlist.java @@ -176,6 +176,6 @@ public class Playlist { ContentValues values = new ContentValues(1); values.put(MediaStore.Audio.Playlists.NAME, newName); - resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, MediaStore.Audio.Playlists._ID + "=" + id, null); + resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, "_id=" + id, null); } } diff --git a/src/org/kreed/vanilla/QueryTask.java b/src/org/kreed/vanilla/QueryTask.java index 0feca4c8..32c4afa2 100644 --- a/src/org/kreed/vanilla/QueryTask.java +++ b/src/org/kreed/vanilla/QueryTask.java @@ -31,9 +31,9 @@ import android.net.Uri; */ public class QueryTask { private Uri mUri; - private String[] mProjection; - private String mSelection; - private String[] mSelectionArgs; + private final String[] mProjection; + private final String mSelection; + private final String[] mSelectionArgs; private String mSortOrder; /** @@ -59,24 +59,6 @@ public class QueryTask { mUri = uri; } - /** - * Modify the projection of the pending query. - * - * @param projection The new projection. - */ - public void setProjection(String[] projection) - { - mProjection = projection; - } - - /** - * Return the sort order of the pending query. - */ - public String getSortOrder() - { - return mSortOrder; - } - /** * Modify the sort order of the pending query. * diff --git a/src/org/kreed/vanilla/Song.java b/src/org/kreed/vanilla/Song.java index f6f5e835..f6a84cee 100644 --- a/src/org/kreed/vanilla/Song.java +++ b/src/org/kreed/vanilla/Song.java @@ -30,9 +30,8 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; - +import android.util.Log; import java.io.FileDescriptor; -import java.io.FileNotFoundException; /** * Represents a Song backed by the MediaStore. Includes basic metadata and @@ -224,8 +223,8 @@ public class Song implements Comparable { FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); cover = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS); } - } catch (IllegalStateException e) { - } catch (FileNotFoundException e) { + } catch (Exception e) { + Log.d("VanillaMusic", "Failed to load cover art for " + path, e); } Bitmap deletedCover = mCoverCache.put(id, cover); diff --git a/src/org/kreed/vanilla/SongTimeline.java b/src/org/kreed/vanilla/SongTimeline.java index a275a442..5336e191 100644 --- a/src/org/kreed/vanilla/SongTimeline.java +++ b/src/org/kreed/vanilla/SongTimeline.java @@ -29,7 +29,6 @@ import android.net.Uri; import android.provider.MediaStore; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -102,12 +101,12 @@ public final class SongTimeline { */ public static final int SHUFFLE_ALBUMS = 2; - private Context mContext; + private final Context mContext; /** * All the songs currently contained in the timeline. Each Song object * should be unique, even if it refers to the same media. */ - private ArrayList mSongs = new ArrayList(); + private ArrayList mSongs = new ArrayList(12); /** * The position of the current song (i.e. the playing song). */ @@ -189,7 +188,7 @@ public final class SongTimeline { * * @param in The stream to read from. */ - public void readState(DataInputStream in) throws IOException, EOFException + public void readState(DataInputStream in) throws IOException { synchronized (this) { int n = in.readInt(); @@ -329,7 +328,7 @@ public final class SongTimeline { saveActiveSongs(); mShuffledSongs = null; mShuffleMode = mode; - if (mode != SHUFFLE_NONE && mFinishAction != FINISH_RANDOM && mSongs.size() != 0) { + if (mode != SHUFFLE_NONE && mFinishAction != FINISH_RANDOM && !mSongs.isEmpty()) { shuffleAll(); ArrayList songs = mShuffledSongs; mShuffledSongs = null; @@ -382,7 +381,7 @@ public final class SongTimeline { Assert.assertTrue(delta >= -1 && delta <= 1); ArrayList timeline = mSongs; - Song song = null; + Song song; synchronized (this) { int pos = mCurrentPos + delta; @@ -441,7 +440,7 @@ public final class SongTimeline { int pos = mCurrentPos + delta; if (mFinishAction != FINISH_RANDOM && pos == mSongs.size()) { - if (mShuffleMode != SHUFFLE_NONE && mSongs.size() > 0) { + if (mShuffleMode != SHUFFLE_NONE && !mSongs.isEmpty()) { if (mShuffledSongs == null) shuffleAll(); mSongs = mShuffledSongs;