Android Wear ChannelApi examples? - wear-os

The latest Android Wear update comes with support for ChannelApi that can be used for sending files to/from wearable or handheld. The problem is I cannot find a single sample of how to use this functionality. The Android samples doesn't include this feature. So if anyone knows how to use the sendFile/receiveFile and can give a quick example here it would be appreciated.

Take a look on this answer to know how to use the Channel API to create the channel between the devices.
After you create the googleClient and retrive the nodeId of the device you want to send the file to, basically you can use the following code on the wearable side:
//opening channel
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleClient, nodeId, "/mypath").await();
channel = result.getChannel();
//sending file
channel.sendFile(googleClient, Uri.fromFile(file));
Then, on the handheld device:
//receiving the file
#Override
public void onChannelOpened(Channel channel) {
if (channel.getPath().equals("/mypath")) {
file = new File("/sdcard/file.txt");
try {
file.createNewFile();
} catch (IOException e) {
//handle error
}
channel.receiveFile(mGoogleApiClient, Uri.fromFile(file), false);
}
}
//when file is ready
#Override
public void onInputClosed(Channel channel, int i, int i1) {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "File received!", Toast.LENGTH_SHORT).show();
}
});
}
If you need more information about this, please visit the reference site from Google

This is just an add to the answer: also check your WearableListenerService in androidmanifest. It's intent filter should contain the com.google.android.gms.wearable.CHANNEL_EVENT action.

I have used some code like this with success. Transfers can be fairly slow.
Both the handheld and wearable applications MUST HAVE the same applicationId in their gradle files.
The wearable needs a manifest entry something like this
<service
android:name="com.me.myWearableListenerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.CHANNEL_EVENT"/>
<data android:scheme="wear" android:host="*" android:pathPrefix="/MyAppPath" />
</intent-filter>
</service>
to launch its WearableListenerService when the Handheld sends a file.
private static final String WEARABLE_FILE_COPY = "MyAppPath/FileCopy";
private void copyFileToWearable (final File file, final String nodeId, Context ctx) {
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onChannelClosed ");
cc.unregisterChannelCallback(this);
}
#Override
public void onOutputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onOutputClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onOutputClosed ");
cc.unregisterChannelCallback(this);
// this is transfer success callback ...
}
};
ChannelClient.Channel c;
Log.d(TAG, "copyFileToWearable transfer file " + file.getName() +
" size:" + file.length()/1000000 + "Mb");
try {
// send the filename to the wearable with the channel open
c = Tasks.await(cc.openChannel(nodeId, WEARABLE_FILE_COPY + "/" + file.getName()));
Log.d(TAG, "copyFileToWearable channel opened to " + nodeId);
Log.d(TAG, "copyFileToWearable register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "copyFileToWearable sending file " + file.getName());
Tasks.await(cc.sendFile(c, Uri.fromFile(file)));
// completion is indicated by onOutputClosed
} catch (Exception e) {
Log.w(TAG, "copyFileToWearable exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure
}
}
}).start();
}
call this from onChannelOpened in a WearableListenerService when c.getPath() starts with WEARABLE_FILE_COPY
private void receiveFileFromHandheld(final ChannelClient.Channel c, File myStorageLocation, Context ctx) {
// filename sent by the handheld is at the end of the path
String[] bits = c.getPath().split("\\/");
// store in a suitable spot
final String receivedFileName = myStorageLocation.getAbsolutePath() + "/" + bits[bits.length-1];
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
boolean mClosed = false;
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onChannelClosed ");
if (!mClosed){
// failure ...
}
}
#Override
public void onInputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onInputClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onInputClosed ");
long fs = new File(receivedFileName).length();
Log.d(TAG, "receiveFileFromHandheld got " + receivedFileName +
" size:" + fs / 1000000 + "Mb");
cc.unregisterChannelCallback(this);
mClosed = true;
// success !
}
};
try {
Log.d(TAG, "receiveFileFromHandheld register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "receiveFileFromHandheld receiving file " + receivedFileName);
Tasks.await(cc.receiveFile(c, Uri.fromFile(new File(receivedFileName)), false));
// completion is indicated by onInputClosed
} catch (Exception e) {
Log.w(TAG, "receiveFileFromHandheld exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure ...
}
}
}
).start();
}

Related

Incorrect file being produced using websockets in helidon

I am trying to upload a file using websockets in Helidon.I think i am doing it write the right way but the code seems to be flaky in terms of the size of the file produced which is different. The size of the file being produced is different for different runs.
How can i make sure that the file size is same on both ends?
I use a simple protocol for handshake[code below]:
Step1 client sends filesize=11000 buffer=5000
Step2 server sends SENDFILE
Step3 client >> buffer 1 server >> write 1 5000
Step4 client >> buffer 2 server >> write 2 5000
Step5 client >> buffer 3 server >> write 3 1000
Step6 client sends ENDOFFILE server >> session.close
//SERVER side OnOpen session below
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
System.out.println("Server >> " + message);
if (message.contains("FILESIZE")) {
session.getBasicRemote().sendText("SENDFILENOW");
}
if(message.contains("ENDOFFILE")) {
System.out.println("Server >> FILE_SIZE=" + FILE_SIZE);
finalFileOutputStream.close();
session.close();
}
}
});
session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {
#Override
public void onMessage(ByteBuffer b) {
finalFileOutputStream.write(b.array(), 0, b.array().length);
finalFileOutputStream.flush();
}
});
//CLIENT OnOpen session below
session.getBasicRemote().sendText("FILESIZE=" + FILE_SIZE);
session.addMessageHandler(new MessageHandler.Whole<String>() {
#Override
public void onMessage(String message) {
long M = FILE_SIZE / BUFFER_SIZE;
long R = FILE_SIZE % BUFFER_SIZE;
if(!message.equals("SENDFILENOW"))
return;
try {
System.out.println("Starting File read ... " + path + " " + FILE_SIZE + " " + M + " " +message );
byte[] buffer = new byte[(int) BUFFER_SIZE];
while (M > 0) {
fileInputStream.read(buffer);
ByteBuffer bytebuffer = ByteBuffer.wrap(buffer);
session.getBasicRemote().sendBinary(bytebuffer);
M--;
}
buffer = new byte[(int) R];
fileInputStream.read(buffer, 0, (int) R);
fileInputStream.close();
ByteBuffer bytebuffer = ByteBuffer.wrap(buffer);
session.getBasicRemote().sendBinary(bytebuffer);
session.getBasicRemote().sendText("FILEREADDONE");
session.close();
f.complete(true);
} catch (IOException e) {
fail("Unexpected exception " + e);
}
}
});
Your solution is unnecessarily built on top of several levels of abstraction just to use websockets. Do you really need that? Helidon is very well equipped to handle huge file upload directly and much more efficiently.
public class LargeUpload {
public static void main(String[] args) {
ExecutorService executor = ThreadPoolSupplier.create("upload-thread-pool").get();
WebServer server = WebServer.builder(Routing.builder()
.post("/streamUpload", (req, res) -> req.content()
.map(DataChunk::data)
.flatMapIterable(Arrays::asList)
.to(IoMulti.writeToFile(createFile(req.queryParams().first("fileName").orElse("bigFile.mkv")))
.executor(executor)
.build())
.onError(res::send)
.onComplete(() -> {
res.status(Http.Status.ACCEPTED_202);
res.send();
}).ignoreElement())
.build())
.port(8080)
.build()
.start()
.await(Duration.ofSeconds(10));
// Server started - do upload
//several gigs file
Path file = Path.of("/home/kec/helidon-kafka.mkv");
try (FileInputStream fis = new FileInputStream(file.toFile())) {
WebClient.builder()
.baseUri("http://localhost:8080")
.build()
.post()
.path("/streamUpload")
.queryParam("fileName", "bigFile_" + System.currentTimeMillis() + ".mkv")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.submit(IoMulti.multiFromByteChannelBuilder(fis.getChannel())
.bufferCapacity(1024 * 1024 * 4)
.build()
.map(DataChunk::create)
)
.await(Duration.ofMinutes(10));
} catch (IOException e) {
throw new RuntimeException(e);
}
executor.shutdown();
server.shutdown()
.await(Duration.ofSeconds(10));
}
static Path createFile(String path) {
try {
Path filePath = Path.of("/home/kec/tmp/" + path);
System.out.println("Creating " + filePath);
return Files.createFile(filePath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

I am using altbeacon library 2.15+ but for oreo and nougat version is not scanning beacon?

onBeacon ServiceConnect method is to detect the beacon. For lollipop version is detecting for nougat and oreo version beacon not detecting:
public void onBeaconServiceConnect() {
RangeNotifier rangeNotifier = new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, org.altbeacon.beacon.Region region) {
Log.d(TAG, "in didRangeBeaconsInRegion " + beacons.size());
if (beacons.size() > 0) {
Log.d(TAG, "didRangeBeaconsInRegion called with beacon count: " + beacons.size());
for (Iterator<Beacon> iterator = beacons.iterator();
iterator.hasNext(); ) {
Beacon beacon = iterator.next();
if (beaconlist.size() > 0) {
Log.d(TAG, "List Size :" + beaconlist.size());
for (int i = 0; i < beaconlist.size(); i++) {
Log.d("BeaconList ", beaconlist.get(i));
}
}
if (!beaconlist.contains(beacon.getId1().toString())) {
Log.d(TAG,"In get APi");
getApi(beacon.getId1().toString());
beaconlist.add(beacon.getId1().toString());
Log.d(TAG, "Notify in dead state");
Log.d("Notify in dead state", beacon.getId1().toString());
}
}
}
}
};
try {
Log.d(TAG, "I am in startRangingBeaconsInRegion");
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
beaconManager.addRangeNotifier(rangeNotifier);
} catch (RemoteException e) {
e.printStackTrace();
}
}
This is my code can any one give solution for this issue.
Two things to check:
Have you added COARSE_LOCATION permission to your AndroidManifest.xml and added code to successfully obtain that permission from the user dynamically at runtime?
Do you successfully construct a BeaconManager, call bind(...) and verify you get a callback to onBeaconServiceConnect() before starting ranging? If you do not, you will get a RemoteException when you try to start ranging. It is a good idea to log this exception to LogCat with Log.e(TAG, "Not bound to beacon scanning service")

pull images from twitter API on processing 3

I am quite new to this so please bear with me. I am trying to use a keyword search to pull images from twitter using twitter4j. I want it to show the images positioned randomly on the screen in a loop.
This code I have below is a combination of different ones I have found online. Its currently finding tweets using these keywords and showing them in the console, however, it is not displaying them on the screen and I’m not sure why…
Another thing is that I think it is doing a live stream from twitter so pulling tweets immediately at the time the code is run, so I am not getting lots of results when I put in an obscure keyword, I want it to search for the last 100 tweets with images using the keyword so that I get more results
I’d really appreciate all the help I can get! thank you
///////////////////////////// Config your setup here! ////////////////////////////
// { site, parse token }
String imageService[][] = {
{
"http://twitpic.com",
"<img class=\"photo\" id=\"photo-display\" src=\""
},
{
"http://twitpic.com",
"<img class=\"photo\" id=\"photo-display\" src=\""
},
{
"http://img.ly",
"<img alt=\"\" id=\"the-image\" src=\""
},
{
"http://lockerz.com/",
"<img id=\"photo\" src=\""
},
{
"http://instagr.am/",
"<meta property=\"og:image\" content=\""
} {
"http://pic.twitter.com",
"<img id
};
// This is where you enter your Oauth info
static String OAuthConsumerKey = KEY_HERE;
static String OAuthConsumerSecret = SECRET_HERE;
static String AccessToken = TOKEN_HERE;
static String AccessTokenSecret = TOKEN_SECRET_HERE;
cb.setIncludeEntitiesEnabled(true);
Twitter twitterInstance = new TwitterFactory(cb.build()).getInstance();
// if you enter keywords here it will filter, otherwise it will sample
String keywords[] = {
"#hashtag" //sample keyword!!!!!!!!
};
///////////////////////////// End Variable Config ////////////////////////////
TwitterStream twitter = new TwitterStreamFactory().getInstance();
PImage img;
boolean imageLoaded;
void setup() {
frameRate(10);
size(800, 600);
noStroke();
imageMode(CENTER);
connectTwitter();
twitter.addListener(listener);
if (keywords.length == 0) twitter.sample();
else twitter.filter(new FilterQuery().track(keywords));
}
void draw() {
background(0);
if (imageLoaded) image(img, width / 5, height / 5);
//image(loadImage((status.getUser().getImage())), (int)random(width*.45), height-(int)random(height*.4));
}
// Initial connection
void connectTwitter() {
twitter.setOAuthConsumer(OAuthConsumerKey, OAuthConsumerSecret);
AccessToken accessToken = loadAccessToken();
twitter.setOAuthAccessToken(accessToken);
}
// Loading up the access token
private static AccessToken loadAccessToken() {
return new AccessToken(AccessToken, AccessTokenSecret);
}
// This listens for new tweet
StatusListener listener = new StatusListener() {
//#Override
public void onStatus(Status status) {
System.out.println("#" + status.getUser().getScreenName() + " - " + status.getText());
}
//#Override
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
}
//#Override
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
}
//#Override
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
}
//#Override
public void onStallWarning(StallWarning warning) {
System.out.println("Got stall warning:" + warning);
}
//#Override
public void onException(Exception ex) {
ex.printStackTrace();
}
};
public void onStatus(Status status) {
String imgUrl = null;
String imgPage = null;
// Checks for images posted using twitter API
if (status.getMediaEntities() != null) {
imgUrl = status.getMediaEntities()[0].getMediaURL().toString();
}
// Checks for images posted using other APIs
else {
if (status.getURLEntities().length > 0) {
if (status.getURLEntities()[0].getExpandedURL() != null) {
imgPage = status.getURLEntities()[0].getExpandedURL().toString();
} else {
if (status.getURLEntities()[0].getDisplayURL() != null) {
imgPage = status.getURLEntities()[0].getDisplayURL().toString();
}
}
}
if (imgPage != null) imgUrl = parseTwitterImg(imgPage);
}
if (imgUrl != null) {
println("found image: " + imgUrl);
// hacks to make image load correctly
if (imgUrl.startsWith("//")) {
println("s3 weirdness");
imgUrl = "http:" + imgUrl;
}
if (!imgUrl.endsWith(".jpg")) {
byte[] imgBytes = loadBytes(imgUrl);
saveBytes("tempImage.jpg", imgBytes);
imgUrl = "tempImage.jpg";
}
println("loading " + imgUrl);
img = loadImage(imgUrl);
imageLoaded = true;
}
}
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
System.out.println("Got track limitation notice:" + numberOfLimitedStatuses);
}
public void onScrubGeo(long userId, long upToStatusId) {
System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
}
public void onException(Exception ex) {
ex.printStackTrace();
}
// Twitter doesn't recognize images from other sites as media, so must be parsed manually
// You can add more services at the top if something is missing
String parseTwitterImg(String pageUrl) {
for (int i = 0; i < imageService.length; i++) {
if (pageUrl.startsWith(imageService[i][0])) {
String fullPage = ""; // container for html
String lines[] = loadStrings(pageUrl); // load html into an array, then move to container
for (int j = 0; j < lines.length; j++) {
fullPage += lines[j] + "\n";
}
String[] pieces = split(fullPage, imageService[i][1]);
pieces = split(pieces[1], "\"");
return (pieces[0]);
}
}
return (null);
}

Transferring assets : Error code 4005 ASSET_UNAVAILABLE

This is driving me crazy. I wrote a code quite a while ago that was working, and opened it again and it happens that I am not able to transfer my assets from the mobile to the wearable device.
public Bitmap loadBitmapFromAsset(Asset asset) {
if (asset == null) {
throw new IllegalArgumentException("Asset must be non-null");
}
// convert asset into a file descriptor and block until it's ready
Log.d(TAG, "api client" + mApiClient);
DataApi.GetFdForAssetResult result = Wearable.DataApi.getFdForAsset(mApiClient, asset).await();
if (result == null) {
Log.w(TAG, "getFdForAsset returned null");
return null;
}
if (result.getStatus().isSuccess()) {
Log.d(TAG, "success");
} else {
Log.d(TAG, result.getStatus().getStatusCode() + ":" + result.getStatus().getStatusMessage());
}
InputStream assetInputStream = result.getInputStream();
if (assetInputStream == null) {
Log.w(TAG, "Requested an unknown Asset.");
return null;
}
// decode the stream into a bitmap
return BitmapFactory.decodeStream(assetInputStream);
}
And this is the code from which I call the loadBitmapFrom Asset method.
DataMap dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
ArrayList<DataMap> dataMaps = dataMap.getDataMapArrayList("dataMaps");
ArrayList<String> names = new ArrayList<>();
ArrayList<String> permalinks = new ArrayList<>();
ArrayList<Asset> images = new ArrayList<>();
for (int i = 0 ; i < dataMaps.size() ; i++) {
Log.d(TAG, dataMaps.get(i).getString("name"));
names.add(dataMaps.get(i).getString("name"));
permalinks.add(dataMaps.get(i).getString("permalink"));
images.add(dataMaps.get(i).getAsset("image"));
}
editor.putInt("my_selection_size", names.size());
for (int i=0; i <names.size() ; i++) {
editor.putString("my_selection_name_" + i, names.get(i));
editor.putString("my_selection_permalink_" + i, permalinks.get(i));
Log.d(TAG, "asset number " + i + " " + images.get(i));
Bitmap bitmap = loadBitmapFromAsset(images.get(i));
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
String encoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
editor.putString("my_selection_image_" + i, encoded);
}
And on the mobile side :
private void sendData(PutDataMapRequest dataMap) {
PutDataRequest request = dataMap.asPutDataRequest();
request.setUrgent();
com.google.android.gms.common.api.PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(mApiClient, request);
pendingResult.setResultCallback(new ResultCallback<DataApi.DataItemResult>() {
#Override
public void onResult(DataApi.DataItemResult dataItemResult) {
com.orange.radio.horizon.tools.Log.d(TAG, "api client : " + mApiClient);
if (dataItemResult.getStatus().isSuccess()) {
com.orange.radio.horizon.tools.Log.d(TAG, "message successfully sent");
} else if (dataItemResult.getStatus().isInterrupted()) {
com.orange.radio.horizon.tools.Log.e(TAG, "couldn't send data to watch (interrupted)");
} else if (dataItemResult.getStatus().isCanceled()) {
com.orange.radio.horizon.tools.Log.e(TAG, "couldn't send data to watch (canceled)");
}
}
});
Log.d(TAG, "Sending data to android wear");
}
class ConfigTask extends AsyncTask<String, Void, String> {
ArrayList<WatchData> mitems;
int mType;
public ConfigTask(ArrayList<WatchData> items, int type)
{
mitems = items;
mType = type;
}
protected String doInBackground(String... str)
{
DataMap dataMap;
ArrayList<DataMap> dataMaps = new ArrayList<>();
Bitmap bitmap = null;
for (int i = 0 ; i < mitems.size() ; i++) {
dataMap = new DataMap();
URL url = null;
try {
url = new URL(mitems.get(i).mUrlSmallLogo);
Log.d(TAG, "url : " + url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
Asset asset = createAssetFromBitmap(bitmap);
dataMap.putAsset("image", asset);
dataMap.putString("name", mitems.get(i).mName);
dataMap.putString("permalink", mitems.get(i).mPermalink);
dataMaps.add(dataMap);
}
PutDataMapRequest request = null;
switch (mType) {
case 0 :
request = PutDataMapRequest.create(SELECTION_PATH);
break;
case 1 :
request = PutDataMapRequest.create(RADIOS_PATH);
break;
case 2 :
request = PutDataMapRequest.create(PODCASTS_PATH);
break;
}
request.getDataMap().putDataMapArrayList("dataMaps", dataMaps);
request.getDataMap().putString("", "" + System.currentTimeMillis()); //random data to refresh
Log.d(TAG, "last bitmap : " + bitmap);
Log.d(TAG, "===============================SENDING THE DATAMAP ARRAYLIST==================================");
sendData(request);
return "h";
}
protected void onPostExecute(String name)
{
}
}
When executing that code, I see the following error happening :
02-02 14:47:59.586 7585-7601/? D/WearMessageListenerService﹕ 4005:ASSET_UNAVAILABLE
I saw that related thread Why does Wearable.DataApi.getFdForAsset produce a result with status 4005 (Asset Unavailable)? but it didn't really help me
I recently had the same problem... I solved it by updating the Google play service, and adding the same signing configuration to both the app and the wearable module. If it doesn't work on the first build go to "invalidate caches / restart" in files and it should work.

Android seekbar getting set to 0 on device rotation

I have a very similar problem like
Seekbar 'unhooking' from media player on orientation change, I get the correct output onSaveInstanceState and onCreateView of my progress bar.
I have implemented a media player in a fragment, on device rotation the song is is working fine but the seekbar progress is getting set to 0. I have done the following.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
//NOTE: When navigating from one fragment to the next
// Bundle/savedInstanceState is always null
// Implemented it using Shared Preferences.
// Always call the superclass so it can save the view hierarchy state
savedInstanceState.putInt(SEEKBAR_PROGRESS, utils.getProgressPercentage(getCurrentPosition(), getDuration()));
Log.i(LOG_TAG, ">>>>> onSaveInstanceState : " + savedInstanceState.getInt(SEEKBAR_PROGRESS));
}
and onCreateView I am checking the savedInstanceState if it is not null and > 0 I am setting the seekbar progress, but it is not working, can someone please tell me why?
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
Bundle arguments = getArguments();
if (arguments != null) {
mUri = arguments.getParcelable(TrackPlayerActivityFragment.DETAIL_URI);
}
final View rootView = inflater.inflate(R.layout.fragment_track_player, container, false);
currentTimeTextView = (TextView) rootView.findViewById(R.id.current_time);
totalTimeView = (TextView) rootView.findViewById(R.id.total_time);
playButtonView = (ToggleButton) rootView.findViewById(R.id.media_play);
Cursor cur = getActivity().getContentResolver().query(mUri,null, null, null, null);
mTrackPlayerAdapter = new TrackPlayerAdapter(getActivity(), cur, 0, this);
mListView = (ListView) rootView.findViewById(R.id.listview_player);
mListView.setAdapter(mTrackPlayerAdapter);
//initialize the play button
playButtonView = (ToggleButton) rootView.findViewById(R.id.media_play);
if(savedInstanceState != null && savedInstanceState.getInt(SEEKBAR_PROGRESS) > 0) {
Log.i(LOG_TAG, ">>>>> onCreateView savedInstance : " + savedInstanceState.getInt(SEEKBAR_PROGRESS));
mSpotifyMusicSeekBar.setProgress(savedInstanceState.getInt(SEEKBAR_PROGRESS));
}
return rootView;
}
the play song is a runnable thread which is working till the completion even on device rotation.
public void playSong(String songUrl, String songTitle) {
Log.i(LOG_TAG, ">>>>> Song URL fragment - " + songUrl);
mSpotifyMusicService.setSongURL(songUrl);
mSpotifyMusicService.setSongTitle(songTitle);
mSpotifyMusicService.playSong();
View v = getActivity().findViewById(R.id.listview_player);
mSpotifyMusicSeekBar = (SeekBar) v.findViewById(R.id.musicSeekBar);
new Thread(new Runnable() {
#Override
public void run() {
try {
int progress = 0;
if(startingPoint > 0) {
progress = startingPoint;
}
while (progress <= 100) {
Thread.sleep(100);
final long totalDuration = getDuration();
progress = utils.getProgressPercentage(getCurrentPosition(), totalDuration);
//set the seekbar position, will be used in saved instance later on
mSpotifyMusicSeekBar.setProgress(progress);
}
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
}
}).start();
//implement the OnSeekBarChangeListener interface methods
mSpotifyMusicSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
Log.i(LOG_TAG, ">>>>> User Progress change" + progress);
mSpotifyMusicService.seek(progress);
} else {
updateMediaPlayerControls(
utils.milliSecondsToTimer(getCurrentPosition()),
utils.milliSecondsToTimer(getDuration())
);
//Log.i(LOG_TAG, ">>>>> System progress %age - " + progress);
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.i("onStartTrackingTouch - ",
"" + seekBar.getProgress());
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
Log.i("onStopTrackingTouch - ",
"" + seekBar.getProgress());
startingPoint = seekBar.getProgress();
mSpotifyMusicService.seek(startingPoint);
}
});
}
The way I solved it was to have the seekbar outside of the custom adapter and made it part of the fragment, and then used onSaveInstanceState to get the percentage and used it onCreateView after checking if the saved instance bundle is not null.

Resources