223
by sindre_mehus
Android: Nicer html in help. |
1 |
/*
|
2 |
This file is part of Subsonic.
|
|
3 |
||
4 |
Subsonic is free software: you can redistribute it and/or modify
|
|
5 |
it under the terms of the GNU General Public License as published by
|
|
6 |
the Free Software Foundation, either version 3 of the License, or
|
|
7 |
(at your option) any later version.
|
|
8 |
||
9 |
Subsonic is distributed in the hope that it will be useful,
|
|
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
GNU General Public License for more details.
|
|
13 |
||
14 |
You should have received a copy of the GNU General Public License
|
|
15 |
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
|
16 |
||
17 |
Copyright 2009 (C) Sindre Mehus
|
|
18 |
*/
|
|
19 |
||
118
by sindre_mehus
Android: Changed package name. |
20 |
package net.sourceforge.subsonic.androidapp.activity; |
21 |
||
533
by Chad Miller
Use FrameLayout in landscape also. |
22 |
import android.app.Activity; |
417
by sindre_mehus
U1M: Authenication. |
23 |
import android.app.AlertDialog; |
533
by Chad Miller
Use FrameLayout in landscape also. |
24 |
import android.content.Context; |
417
by sindre_mehus
U1M: Authenication. |
25 |
import android.content.DialogInterface; |
118
by sindre_mehus
Android: Changed package name. |
26 |
import android.content.Intent; |
417
by sindre_mehus
U1M: Authenication. |
27 |
import android.content.SharedPreferences; |
28 |
import android.net.Uri; |
|
418
by sindre_mehus
U1M: Authenication. |
29 |
import android.os.Build; |
118
by sindre_mehus
Android: Changed package name. |
30 |
import android.os.Bundle; |
494
by Chad Miller
Add debug menu support when /sdcard//subsonic/showdebugmenu file exists. |
31 |
import android.os.Environment; |
118
by sindre_mehus
Android: Changed package name. |
32 |
import android.preference.PreferenceManager; |
533
by Chad Miller
Use FrameLayout in landscape also. |
33 |
import android.util.Log; |
261
by sindre_mehus
Android: Refactor server selection. |
34 |
import android.view.ContextMenu; |
533
by Chad Miller
Use FrameLayout in landscape also. |
35 |
import android.view.KeyEvent; |
287
by sindre_mehus
Android: Main layout. |
36 |
import android.view.LayoutInflater; |
437.1.2
by Chad Miller
Declutter the screen. Exit and help buttons should be in the menu, not always visible in the top. |
37 |
import android.view.Menu; |
278
by sindre_mehus
Android: Created playlist + search tabs. |
38 |
import android.view.MenuItem; |
529
by Chad Miller
first of initial UI revolution |
39 |
import android.view.MotionEvent; |
118
by sindre_mehus
Android: Changed package name. |
40 |
import android.view.View; |
533
by Chad Miller
Use FrameLayout in landscape also. |
41 |
import android.view.ViewGroup; |
118
by sindre_mehus
Android: Changed package name. |
42 |
import android.view.Window; |
533
by Chad Miller
Use FrameLayout in landscape also. |
43 |
import android.widget.AdapterView; |
529
by Chad Miller
first of initial UI revolution |
44 |
import android.widget.ArrayAdapter; |
533
by Chad Miller
Use FrameLayout in landscape also. |
45 |
import android.widget.ImageView; |
287
by sindre_mehus
Android: Main layout. |
46 |
import android.widget.ListView; |
533
by Chad Miller
Use FrameLayout in landscape also. |
47 |
import android.widget.ProgressBar; |
48 |
import android.widget.SlidingDrawer; |
|
223
by sindre_mehus
Android: Nicer html in help. |
49 |
import android.widget.TextView; |
529
by Chad Miller
first of initial UI revolution |
50 |
import android.widget.ViewFlipper; |
533
by Chad Miller
Use FrameLayout in landscape also. |
51 |
import net.sourceforge.subsonic.androidapp.domain.Artist; |
52 |
import net.sourceforge.subsonic.androidapp.domain.Indexes; |
|
388
by sindre_mehus
Android: Minor bugfixes in offline mode. |
53 |
import net.sourceforge.subsonic.androidapp.service.DownloadService; |
168
by sindre_mehus
Android: Refactored big time. |
54 |
import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl; |
533
by Chad Miller
Use FrameLayout in landscape also. |
55 |
import net.sourceforge.subsonic.androidapp.service.MusicService; |
56 |
import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory; |
|
57 |
import net.sourceforge.subsonic.androidapp.util.ArtistAdapter; |
|
58 |
import net.sourceforge.subsonic.androidapp.util.BackgroundTask; |
|
406.1.51
by sindre_mehus
Android: Update SackOfViewsAdapter, use MergeAdapter instead in MainActivity. |
59 |
import net.sourceforge.subsonic.androidapp.util.Constants; |
60 |
import net.sourceforge.subsonic.androidapp.util.MergeAdapter; |
|
533
by Chad Miller
Use FrameLayout in landscape also. |
61 |
import net.sourceforge.subsonic.androidapp.util.MainActivityBackgroundTask; |
193
by sindre_mehus
Android: Improved offline mode. |
62 |
import net.sourceforge.subsonic.androidapp.util.Util; |
412
by sindre_mehus
U1M: Use proper import for R resource class. |
63 |
import net.sourceforge.subsonic.u1m.R; |
279
by sindre_mehus
Android: Button bar + theme. |
64 |
|
519
by Chad Miller
Start adding analytics to Music app. I don't know how some of these traits will graph, so I'll revisit these later. |
65 |
import net.sourceforge.subsonic.u1m.androidapp.Analytics; |
66 |
import com.google.android.apps.analytics.GoogleAnalyticsTracker; |
|
67 |
||
494
by Chad Miller
Add debug menu support when /sdcard//subsonic/showdebugmenu file exists. |
68 |
import java.io.File; |
414
by sindre_mehus
U1M: Hidden server settings. |
69 |
import java.util.ArrayList; |
279
by sindre_mehus
Android: Button bar + theme. |
70 |
import java.util.Arrays; |
533
by Chad Miller
Use FrameLayout in landscape also. |
71 |
import java.util.LinkedList; |
72 |
import java.util.List; |
|
118
by sindre_mehus
Android: Changed package name. |
73 |
|
529
by Chad Miller
first of initial UI revolution |
74 |
public class MainActivity extends Activity implements View.OnTouchListener { |
75 |
private static final String TAG = MainActivity.class.getSimpleName(); |
|
76 |
||
77 |
private long startupTime; |
|
78 |
private float beginTouchX = 0; |
|
530
by Chad Miller
Add some data to test with. Open drawer. |
79 |
private float beginTouchY = 0; |
529
by Chad Miller
first of initial UI revolution |
80 |
private ViewFlipper vf; |
81 |
private int vfArtistsChildIndex; |
|
82 |
private int vfSongsChildIndex; |
|
535
by Chad Miller
Fix indentation. |
83 |
private boolean destroyed; |
533
by Chad Miller
Use FrameLayout in landscape also. |
84 |
|
85 |
private ListView artistsListView; |
|
537
by Chad Miller
Start to plug in the song list. |
86 |
private ListView songsListView; |
529
by Chad Miller
first of initial UI revolution |
87 |
|
531
by Chad Miller
Use a FrameLayout so the SlidingDrawer works. |
88 |
private TextView songsListTitle; |
89 |
||
529
by Chad Miller
first of initial UI revolution |
90 |
/**
|
91 |
* Called when the activity is first created.
|
|
92 |
*/
|
|
93 |
@Override
|
|
94 |
public void onCreate(Bundle savedInstanceState) { |
|
95 |
super.onCreate(savedInstanceState); |
|
96 |
setContentView(R.layout.new_main); |
|
97 |
||
537
by Chad Miller
Start to plug in the song list. |
98 |
this.artistsListView = (ListView) findViewById(R.id.artists_list); |
529
by Chad Miller
first of initial UI revolution |
99 |
vf = (ViewFlipper) findViewById(R.id.top_flipper); |
100 |
||
101 |
if (vf == null) { |
|
102 |
Log.e(TAG, "no vf"); |
|
103 |
} else { |
|
104 |
vfArtistsChildIndex = vf.indexOfChild(findViewById(R.id.top_flipper_child_artists)); |
|
105 |
vfSongsChildIndex = vf.indexOfChild(findViewById(R.id.top_flipper_child_songs)); |
|
106 |
vf.setOnTouchListener(this); |
|
107 |
}
|
|
108 |
||
537
by Chad Miller
Start to plug in the song list. |
109 |
songsListTitle = new TextView(this); |
110 |
//songsListTitle.setHint("unset"); // FIXME string resource
|
|
111 |
this.songsListView = (ListView) findViewById(R.id.songs_grouped_by_albums_list); |
|
112 |
//this.songsListView.addHeaderView(songsListTitle);
|
|
113 |
this.songsListView.setFastScrollEnabled(true); |
|
114 |
this.songsListView.setOnTouchListener(this); |
|
115 |
||
533
by Chad Miller
Use FrameLayout in landscape also. |
116 |
final TextView artistsListTitle = new TextView(this); |
537
by Chad Miller
Start to plug in the song list. |
117 |
this.artistsListView = (ListView) findViewById(R.id.artists_list); |
118 |
artistsListTitle.setText("Artists"); // FIXME string resource |
|
119 |
this.artistsListView.addHeaderView(artistsListTitle); |
|
120 |
this.artistsListView.setFastScrollEnabled(true); |
|
121 |
this.artistsListView.setOnTouchListener(this); |
|
122 |
this.artistsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
|
531
by Chad Miller
Use a FrameLayout so the SlidingDrawer works. |
123 |
@Override
|
533
by Chad Miller
Use FrameLayout in landscape also. |
124 |
public void onItemClick(AdapterView parent, View view, int position, long id) { |
537
by Chad Miller
Start to plug in the song list. |
125 |
final Artist pickedArtist = (Artist) parent.getItemAtPosition(position); |
126 |
MainActivity.this.songsListTitle.setText(pickedArtist.getName()); |
|
531
by Chad Miller
Use a FrameLayout so the SlidingDrawer works. |
127 |
MainActivity.this.flipToSongs(); |
537
by Chad Miller
Start to plug in the song list. |
128 |
loadSongs(MainActivity.this.songsListView, pickedArtist.getId()); |
531
by Chad Miller
Use a FrameLayout so the SlidingDrawer works. |
129 |
}
|
130 |
});
|
|
537
by Chad Miller
Start to plug in the song list. |
131 |
loadArtists(this.artistsListView); |
530
by Chad Miller
Add some data to test with. Open drawer. |
132 |
|
533
by Chad Miller
Use FrameLayout in landscape also. |
133 |
final SlidingDrawer playQueueSlidingDrawer = (SlidingDrawer) findViewById(R.id.play_queue_drawer); |
529
by Chad Miller
first of initial UI revolution |
134 |
|
135 |
super.onCreate(savedInstanceState); |
|
136 |
}
|
|
137 |
||
138 |
@Override
|
|
139 |
public boolean onKeyDown(int keyCode, KeyEvent event) { |
|
140 |
final int currentFlipperPosition; |
|
141 |
if (vf != null) { |
|
142 |
currentFlipperPosition = vf.getDisplayedChild(); |
|
143 |
} else { |
|
144 |
currentFlipperPosition = -1; |
|
145 |
}
|
|
146 |
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { |
|
147 |
if ((vf != null) && (currentFlipperPosition != vfArtistsChildIndex)) { |
|
148 |
flipToArtists(); |
|
149 |
return true; |
|
150 |
}
|
|
151 |
}
|
|
152 |
if (keyCode == KeyEvent.KEYCODE_SEARCH) { |
|
153 |
exposeSearchBar(); |
|
154 |
if ((vf != null) && (currentFlipperPosition != vfSongsChildIndex)) { |
|
155 |
flipToSongs(); |
|
156 |
}
|
|
157 |
return true; |
|
158 |
}
|
|
159 |
return super.onKeyDown(keyCode, event); |
|
160 |
}
|
|
161 |
||
537
by Chad Miller
Start to plug in the song list. |
162 |
private void loadSongs(ListView songsListView, String artistId) { |
163 |
Log.v(TAG, String.format("loadSongs(%s, %s)", songsListView, artistId)); |
|
164 |
}
|
|
533
by Chad Miller
Use FrameLayout in landscape also. |
165 |
|
166 |
private void loadArtists(ListView artistList) { |
|
167 |
final List<Artist> artists = new LinkedList<Artist>(); |
|
168 |
artistList.setAdapter(new ArtistAdapter(this, artists)); |
|
169 |
||
170 |
final MusicService musicService = MusicServiceFactory.getMusicService(MainActivity.this); |
|
171 |
Indexes cacheResult = null; |
|
172 |
try { |
|
173 |
cacheResult = musicService.getFastIndexes(MainActivity.this); |
|
174 |
} catch (Exception ex) { |
|
175 |
Log.d(TAG, "Loading cache for view adapter failed", ex); |
|
176 |
}
|
|
177 |
||
178 |
final long lastModified; |
|
179 |
if (cacheResult != null) { |
|
180 |
artists.addAll(cacheResult.getShortcuts()); |
|
181 |
artists.addAll(cacheResult.getArtists()); |
|
182 |
lastModified = cacheResult.getLastModified(); |
|
183 |
} else { |
|
184 |
lastModified = 0L; |
|
185 |
Log.d(TAG, "There is no usable cache of artists."); |
|
186 |
}
|
|
187 |
||
188 |
BackgroundTask<Indexes> task = new MainActivityBackgroundTask<Indexes>(this) { |
|
189 |
@Override
|
|
190 |
protected Indexes doInBackground() throws Throwable { |
|
191 |
boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false); |
|
192 |
return musicService.getIndexes(refresh, MainActivity.this, this, lastModified); |
|
193 |
}
|
|
194 |
||
195 |
@Override
|
|
196 |
protected void done(Indexes freshResult) { |
|
197 |
artists.clear(); // !? |
|
198 |
if (freshResult != null) { |
|
199 |
// A Set would be so much better. TODO
|
|
200 |
artists.addAll(freshResult.getShortcuts()); |
|
201 |
artists.addAll(freshResult.getArtists()); |
|
202 |
} else { |
|
203 |
Log.i(TAG, "No fresh artists to update view with."); |
|
204 |
}
|
|
205 |
}
|
|
206 |
};
|
|
207 |
task.execute(); |
|
208 |
}
|
|
209 |
||
210 |
public void setProgressVisible(boolean visible) { |
|
211 |
View view = findViewById(R.id.tab_progress); |
|
212 |
if (view != null) { |
|
213 |
view.setVisibility(visible ? View.VISIBLE : View.GONE); |
|
214 |
}
|
|
215 |
||
216 |
if (! visible) { |
|
217 |
updateProgress(null); |
|
218 |
}
|
|
219 |
}
|
|
220 |
||
221 |
public void updateProgress(String message) { |
|
222 |
ImageView refreshIcon = (ImageView) findViewById(R.id.refresh_icon); |
|
223 |
ProgressBar progressBar = (ProgressBar) findViewById(R.id.loading_progress); |
|
224 |
TextView headerDescriptionView = (TextView) findViewById(R.id.header_description); |
|
225 |
||
226 |
if (headerDescriptionView == null) { |
|
227 |
// Some screens do not have headers for the progress or state or verb for reload.
|
|
228 |
return; |
|
229 |
}
|
|
230 |
||
231 |
if (message != null) { |
|
232 |
headerDescriptionView.setText(message); |
|
233 |
if (refreshIcon != null) { refreshIcon.setVisibility(View.GONE); } |
|
234 |
if (progressBar != null) { progressBar.setVisibility(View.VISIBLE); } |
|
235 |
} else { |
|
236 |
headerDescriptionView.setText(getResources().getString(R.string.select_artist_refresh)); |
|
237 |
if (refreshIcon != null) { refreshIcon.setVisibility(View.VISIBLE); } |
|
238 |
if (progressBar != null) { progressBar.setVisibility(View.GONE); } |
|
239 |
}
|
|
240 |
}
|
|
241 |
||
242 |
public boolean isDestroyed() { |
|
243 |
return destroyed; |
|
244 |
}
|
|
245 |
||
246 |
@Override
|
|
247 |
protected void onDestroy() { |
|
248 |
super.onDestroy(); |
|
249 |
// if (this.tracker != null) {
|
|
250 |
// this.tracker.dispatch();
|
|
251 |
// this.tracker.stop();
|
|
252 |
// }
|
|
253 |
destroyed = true; |
|
254 |
}
|
|
255 |
||
529
by Chad Miller
first of initial UI revolution |
256 |
void exposeSearchBar() { } |
257 |
||
258 |
@Override
|
|
259 |
public boolean onTouch(View view, MotionEvent motion) { |
|
530
by Chad Miller
Add some data to test with. Open drawer. |
260 |
if (vf == null) { |
261 |
return false; |
|
262 |
}
|
|
529
by Chad Miller
first of initial UI revolution |
263 |
final int currentFlipperPosition = vf.getDisplayedChild(); |
264 |
final int flipperPositionCount = vf.getChildCount(); |
|
265 |
switch (motion.getAction()) { |
|
266 |
case MotionEvent.ACTION_DOWN: |
|
267 |
{
|
|
268 |
beginTouchX = motion.getX(); |
|
530
by Chad Miller
Add some data to test with. Open drawer. |
269 |
beginTouchY = motion.getY(); |
529
by Chad Miller
first of initial UI revolution |
270 |
return true; |
271 |
}
|
|
272 |
case MotionEvent.ACTION_UP: |
|
273 |
{
|
|
530
by Chad Miller
Add some data to test with. Open drawer. |
274 |
final float nowTouchX = motion.getX(); |
275 |
final float nowTouchY = motion.getY(); |
|
276 |
final float deltaX = nowTouchX - beginTouchX; |
|
277 |
final float deltaY = nowTouchY - beginTouchY; |
|
278 |
if (Math.abs(deltaY) > Math.abs(deltaX)) { |
|
279 |
// More up/down distance than right/left distance.
|
|
280 |
return false; |
|
281 |
}
|
|
537
by Chad Miller
Start to plug in the song list. |
282 |
//Log.d(TAG, "touch delta X is " + deltaX);
|
530
by Chad Miller
Add some data to test with. Open drawer. |
283 |
if (Math.abs(deltaX) < 40) { // TODO What threshold? |
284 |
return false; |
|
285 |
}
|
|
529
by Chad Miller
first of initial UI revolution |
286 |
if (nowTouchX > beginTouchX) { |
287 |
if (currentFlipperPosition != vfArtistsChildIndex) { |
|
288 |
flipToArtists(); |
|
530
by Chad Miller
Add some data to test with. Open drawer. |
289 |
return true; |
529
by Chad Miller
first of initial UI revolution |
290 |
}
|
291 |
}
|
|
292 |
if (nowTouchX < beginTouchX) { |
|
293 |
if (currentFlipperPosition != vfSongsChildIndex) { |
|
294 |
flipToSongs(); |
|
530
by Chad Miller
Add some data to test with. Open drawer. |
295 |
return true; |
529
by Chad Miller
first of initial UI revolution |
296 |
}
|
297 |
}
|
|
298 |
}
|
|
299 |
}
|
|
300 |
return false; |
|
301 |
}
|
|
302 |
||
303 |
void flipToDestination(int targetView) { |
|
537
by Chad Miller
Start to plug in the song list. |
304 |
//Log.d(TAG, "at pos " + vf.getDisplayedChild() + "and want pos " + targetView);
|
529
by Chad Miller
first of initial UI revolution |
305 |
while (vf.getDisplayedChild() > targetView) { |
306 |
vf.setInAnimation(MainActivity.this, R.anim.transition_in_right); |
|
307 |
vf.setOutAnimation(MainActivity.this, R.anim.transition_out_right); |
|
308 |
vf.showPrevious(); |
|
309 |
}
|
|
310 |
while (vf.getDisplayedChild() < targetView) { |
|
311 |
vf.setInAnimation(MainActivity.this, R.anim.transition_in_left); |
|
312 |
vf.setOutAnimation(MainActivity.this, R.anim.transition_out_left); |
|
313 |
vf.showNext(); |
|
314 |
}
|
|
315 |
}
|
|
316 |
||
317 |
void flipToArtists() { |
|
318 |
flipToDestination(vfArtistsChildIndex); |
|
319 |
}
|
|
320 |
||
321 |
void flipToSongs() { |
|
322 |
flipToDestination(vfSongsChildIndex); |
|
323 |
}
|
|
324 |
||
442
by Chad Miller
Bump version for official release. |
325 |
}
|