mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 09:01:55 +01:00
Huawei: Initial music managment support
This commit is contained in:
parent
1a3a7dec05
commit
dc1533b4ed
@ -199,6 +199,10 @@
|
||||
android:label="@string/title_activity_appmanager"
|
||||
android:launchMode="singleTop"
|
||||
android:parentActivityName=".activities.ControlCenterv2" />
|
||||
<activity
|
||||
android:name=".activities.musicmanager.MusicManagerActivity"
|
||||
android:label="@string/title_activity_musicmanager"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.AppBlacklistActivity"
|
||||
android:label="@string/title_activity_notification_management"
|
||||
|
@ -285,6 +285,8 @@ public class DeviceSettingsPreferenceConst {
|
||||
public static final String PREF_CONTACTS = "pref_contacts";
|
||||
public static final String PREF_WIDGETS = "pref_widgets";
|
||||
|
||||
public static final String PREF_MUSIC_MANAGEMENT = "pref_music_management";
|
||||
|
||||
public static final String PREF_ANTILOST_ENABLED = "pref_antilost_enabled";
|
||||
public static final String PREF_HYDRATION_SWITCH = "pref_hydration_switch";
|
||||
public static final String PREF_HYDRATION_PERIOD = "pref_hydration_period";
|
||||
|
@ -70,6 +70,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureWorldClocks;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.app_specific_notifications.AppSpecificNotificationSettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.loyaltycards.LoyaltyCardsSettingsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.loyaltycards.LoyaltyCardsSettingsConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.musicmanager.MusicManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.widgets.WidgetScreensListActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl;
|
||||
@ -1051,6 +1052,19 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
|
||||
});
|
||||
}
|
||||
|
||||
final Preference music_management = findPreference(PREF_MUSIC_MANAGEMENT);
|
||||
if (music_management != null) {
|
||||
music_management.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
final Intent intent = new Intent(getContext(), MusicManagerActivity.class);
|
||||
intent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final Preference widgets = findPreference(PREF_WIDGETS);
|
||||
if (widgets != null) {
|
||||
widgets.setOnPreferenceClickListener(preference -> {
|
||||
|
@ -0,0 +1,632 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities.musicmanager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FwAppInstallerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.MusicListAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusicPlaylist;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GridAutoFitLayoutManager;
|
||||
|
||||
public class MusicManagerActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MusicManagerActivity.class);
|
||||
|
||||
public static final String ACTION_MUSIC_DATA
|
||||
= "nodomain.freeyourgadget.gadgetbridge.musicmanager.action.music_data";
|
||||
public static final String ACTION_MUSIC_UPDATE
|
||||
= "nodomain.freeyourgadget.gadgetbridge.musicmanager.action.music_update";
|
||||
|
||||
protected GBDevice mGBDevice = null;
|
||||
|
||||
private View loadingView = null;
|
||||
private TextView musicDeviceInfo = null;
|
||||
|
||||
private final List<GBDeviceMusic> allMusic = new ArrayList<>();
|
||||
|
||||
private final List<GBDeviceMusic> musicList = new ArrayList<>();
|
||||
private MusicListAdapter musicAdapter;
|
||||
|
||||
private final List<GBDeviceMusicPlaylist> playlists = new ArrayList<>();
|
||||
private ArrayAdapter<GBDeviceMusicPlaylist> playlistAdapter;
|
||||
|
||||
private View playlistSpinnerLayout;
|
||||
private Spinner playlistsSpinner;
|
||||
|
||||
private FloatingActionButton fabMusicUpload;
|
||||
private FloatingActionButton fabMusicPlaylistAdd;
|
||||
|
||||
private int maxMusicCount = 0;
|
||||
private int maxPlaylistCount = 0;
|
||||
|
||||
public GBDevice getGBDevice() {
|
||||
return mGBDevice;
|
||||
}
|
||||
|
||||
Handler loadingTimeout = new Handler();
|
||||
Runnable loadingRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GB.toast(getString(R.string.music_error), Toast.LENGTH_SHORT, GB.ERROR);
|
||||
stopLoading();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_musicmanager);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
}
|
||||
if (mGBDevice == null) {
|
||||
throw new IllegalArgumentException("Must provide a device when invoking this activity");
|
||||
}
|
||||
|
||||
fabMusicUpload = findViewById(R.id.fab_music_upload);
|
||||
assert fabMusicUpload != null;
|
||||
fabMusicUpload.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("audio/*");
|
||||
openAudioActivityResultLauncher.launch(intent);
|
||||
}
|
||||
});
|
||||
|
||||
fabMusicPlaylistAdd = findViewById(R.id.fab_music_playlist_add);
|
||||
assert fabMusicPlaylistAdd != null;
|
||||
fabMusicPlaylistAdd.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
addMusicPlaylist();
|
||||
}
|
||||
});
|
||||
|
||||
hideActionButtons();
|
||||
|
||||
RecyclerView musicListView = findViewById(R.id.music_songs_list);
|
||||
loadingView = findViewById(R.id.music_loading);
|
||||
|
||||
musicDeviceInfo = findViewById(R.id.music_device_info);
|
||||
|
||||
musicListView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
if (dy > 0) {
|
||||
hideActionButtons();
|
||||
} else if (dy < 0) {
|
||||
showActionButtons();
|
||||
}
|
||||
}
|
||||
});
|
||||
musicListView.setLayoutManager(new GridAutoFitLayoutManager(this, 300));
|
||||
|
||||
musicAdapter = new MusicListAdapter(
|
||||
musicList,
|
||||
new MusicListAdapter.onItemAction() {
|
||||
@Override
|
||||
public void onItemClick(View view, GBDeviceMusic music) {
|
||||
openPopupMenu(view, music);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(View view, GBDeviceMusic music) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
musicListView.setAdapter(musicAdapter);
|
||||
|
||||
playlistSpinnerLayout = findViewById(R.id.music_playlists_layout);
|
||||
|
||||
playlistsSpinner = findViewById(R.id.music_playlists);
|
||||
|
||||
ImageButton renamePlaylist = findViewById(R.id.music_playlist_rename);
|
||||
assert renamePlaylist != null;
|
||||
renamePlaylist.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
renameMusicPlaylist((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem());
|
||||
}
|
||||
});
|
||||
|
||||
ImageButton deletePlaylist = findViewById(R.id.music_playlist_delete);
|
||||
assert deletePlaylist != null;
|
||||
deletePlaylist.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
deleteMusicPlaylist((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
playlistsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
GBDeviceMusicPlaylist item = (GBDeviceMusicPlaylist) adapterView.getItemAtPosition(i);
|
||||
if (item.getId() == 0) {
|
||||
deletePlaylist.setVisibility(View.GONE);
|
||||
renamePlaylist.setVisibility(View.GONE);
|
||||
|
||||
} else {
|
||||
deletePlaylist.setVisibility(View.VISIBLE);
|
||||
renamePlaylist.setVisibility(View.VISIBLE);
|
||||
}
|
||||
updateCurrentMusicList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
playlistAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, playlists);
|
||||
initPlaylists();
|
||||
|
||||
playlistAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
playlistsSpinner.setAdapter(playlistAdapter);
|
||||
}
|
||||
|
||||
private void hideActionButtons() {
|
||||
fabMusicUpload.hide();
|
||||
fabMusicPlaylistAdd.hide();
|
||||
|
||||
}
|
||||
|
||||
private void showActionButtons() {
|
||||
fabMusicUpload.show();
|
||||
if(maxPlaylistCount > 0) {
|
||||
fabMusicPlaylistAdd.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void startLoading(long timeout) {
|
||||
hideActionButtons();
|
||||
loadingView.setVisibility(View.VISIBLE);
|
||||
if(timeout > 0) {
|
||||
loadingTimeout.postDelayed(loadingRunnable, timeout);
|
||||
}
|
||||
}
|
||||
private void startLoading() {
|
||||
startLoading(4000);
|
||||
}
|
||||
|
||||
private void stopLoading() {
|
||||
loadingTimeout.removeCallbacks(loadingRunnable);
|
||||
loadingView.setVisibility(View.GONE);
|
||||
showActionButtons();
|
||||
}
|
||||
|
||||
private void updateCurrentMusicList() {
|
||||
GBDeviceMusicPlaylist current = (GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem();
|
||||
musicList.clear();
|
||||
if (current.getId() == 0) {
|
||||
musicList.addAll(allMusic);
|
||||
} else {
|
||||
List<GBDeviceMusic> filtered = allMusic.stream().filter(m -> current.getMusicIds().contains(m.getId())).collect(Collectors.toList());
|
||||
musicList.addAll(filtered);
|
||||
}
|
||||
musicAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void initPlaylists() {
|
||||
playlists.clear();
|
||||
playlists.add(new GBDeviceMusicPlaylist(0,this.getString(R.string.music_all_songs),null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_MUSIC_DATA);
|
||||
filter.addAction(ACTION_MUSIC_UPDATE);
|
||||
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
||||
|
||||
// Load music data without timeout
|
||||
startLoading(0);
|
||||
GBApplication.deviceService(mGBDevice).onMusicListReq();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
ActivityResultLauncher<Intent> openAudioActivityResultLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
new ActivityResultCallback<ActivityResult>() {
|
||||
@Override
|
||||
public void onActivityResult(ActivityResult result) {
|
||||
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
||||
Intent startIntent = new Intent(MusicManagerActivity.this, FwAppInstallerActivity.class);
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
startIntent.setDataAndType(result.getData().getData(), null);
|
||||
startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
public boolean openPopupMenu(View view, GBDeviceMusic music) {
|
||||
PopupMenu popupMenu = new PopupMenu(this, view);
|
||||
popupMenu.getMenuInflater().inflate(R.menu.musicmanager_context, popupMenu.getMenu());
|
||||
Menu menu = popupMenu.getMenu();
|
||||
|
||||
if (playlists.size() <= 1) {
|
||||
menu.removeItem(R.id.musicmanager_add_to_playlist);
|
||||
}
|
||||
|
||||
GBDeviceMusicPlaylist current = (GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem();
|
||||
musicList.clear();
|
||||
if (current.getId() == 0) {
|
||||
menu.removeItem(R.id.musicmanager_delete_from_playlist);
|
||||
} else {
|
||||
menu.removeItem(R.id.musicmanager_delete);
|
||||
}
|
||||
|
||||
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return onPopupItemSelected(item, music);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
popupMenu.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onPopupItemSelected(final MenuItem item, final GBDeviceMusic music) {
|
||||
final int itemId = item.getItemId();
|
||||
if (itemId == R.id.musicmanager_delete || itemId == R.id.musicmanager_delete_from_playlist) {
|
||||
deleteMusicConfirm(music);
|
||||
return true;
|
||||
} else if (itemId == R.id.musicmanager_add_to_playlist) {
|
||||
addMusicSongToPlaylist(music);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void deleteMusicConfirm(final GBDeviceMusic music) {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.Delete)
|
||||
.setMessage(this.getString(R.string.music_delete_confirm_description, music.getTitle()))
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
deleteMusicFromDevice((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem(), music);
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void addPlaylistToDevice(final String playlistName) {
|
||||
startLoading();
|
||||
GBApplication.deviceService(mGBDevice).onMusicOperation(0, -1, playlistName, null);
|
||||
}
|
||||
|
||||
private void deletePlaylistFromDevice(final GBDeviceMusicPlaylist playlist) {
|
||||
startLoading();
|
||||
GBApplication.deviceService(mGBDevice).onMusicOperation(1, playlist.getId(), null, null);
|
||||
}
|
||||
|
||||
private void renamePlaylistOnDevice(final GBDeviceMusicPlaylist playlist, String newPlaylistName) {
|
||||
startLoading();
|
||||
GBApplication.deviceService(mGBDevice).onMusicOperation(2, playlist.getId(), newPlaylistName, null);
|
||||
}
|
||||
|
||||
private void addMusicToDevicePlaylist(GBDeviceMusicPlaylist playlist, final GBDeviceMusic music) {
|
||||
startLoading();
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
list.add(music.getId());
|
||||
GBApplication.deviceService(mGBDevice).onMusicOperation(3, playlist.getId(), null, list);
|
||||
}
|
||||
|
||||
private void deleteMusicFromDevice(GBDeviceMusicPlaylist playlist, final GBDeviceMusic music) {
|
||||
startLoading();
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
list.add(music.getId());
|
||||
GBApplication.deviceService(mGBDevice).onMusicOperation(4, playlist.getId(), null, list);
|
||||
}
|
||||
|
||||
private void addMusicPlaylist() {
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
|
||||
FrameLayout container = new FrameLayout(this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
input.setLayoutParams(params);
|
||||
container.addView(input);
|
||||
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.music_new_playlist)
|
||||
.setView(container)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
|
||||
String playlistName = input.getText().toString();
|
||||
addPlaylistToDevice(playlistName);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void renameMusicPlaylist(GBDeviceMusicPlaylist playlist) {
|
||||
if(playlist.getId() == 0)
|
||||
return;
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(playlist.getName());
|
||||
|
||||
FrameLayout container = new FrameLayout(this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
input.setLayoutParams(params);
|
||||
container.addView(input);
|
||||
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.music_rename_playlist)
|
||||
.setView(container)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
|
||||
String playlistName = input.getText().toString();
|
||||
renamePlaylistOnDevice(playlist, playlistName);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void deleteMusicPlaylist(GBDeviceMusicPlaylist playlist) {
|
||||
if(playlist.getId() == 0)
|
||||
return;
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.Delete)
|
||||
.setMessage(this.getString(R.string.music_delete_confirm_description, playlist.getName()))
|
||||
.setIcon(R.drawable.ic_warning)
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
deletePlaylistFromDevice(playlist);
|
||||
})
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void addMusicSongToPlaylist(final GBDeviceMusic music) {
|
||||
final Spinner dPlaylists = new Spinner(this);
|
||||
|
||||
List<GBDeviceMusicPlaylist> dialogPlaylists = new ArrayList<>();
|
||||
for (GBDeviceMusicPlaylist playlist : playlists) {
|
||||
if (playlist.getId() != 0) {
|
||||
dialogPlaylists.add(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayAdapter<GBDeviceMusicPlaylist> dialogPlaylistAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, dialogPlaylists);
|
||||
dialogPlaylistAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
dPlaylists.setAdapter(dialogPlaylistAdapter);
|
||||
|
||||
FrameLayout container = new FrameLayout(this);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||
dPlaylists.setLayoutParams(params);
|
||||
container.addView(dPlaylists);
|
||||
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.music_add_to_playlist)
|
||||
.setView(container)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
|
||||
GBDeviceMusicPlaylist playlist = (GBDeviceMusicPlaylist) dPlaylists.getSelectedItem();
|
||||
addMusicToDevicePlaylist(playlist, music);
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void startSyncFromDevice(Intent intent) {
|
||||
String info = intent.getStringExtra("deviceInfo");
|
||||
if (!TextUtils.isEmpty(info)) {
|
||||
musicDeviceInfo.setText(info);
|
||||
} else {
|
||||
musicDeviceInfo.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
maxMusicCount = intent.getIntExtra("maxMusicCount", 0);
|
||||
maxPlaylistCount = intent.getIntExtra("maxPlaylistCount", 0);
|
||||
|
||||
// Hide playlist if device does not support it.
|
||||
playlistSpinnerLayout.setVisibility(maxPlaylistCount>0?View.VISIBLE:View.GONE);
|
||||
|
||||
allMusic.clear();
|
||||
musicList.clear();
|
||||
initPlaylists();
|
||||
}
|
||||
|
||||
private void musicListFromDevice(Intent intent) {
|
||||
ArrayList<GBDeviceMusic> list = (ArrayList<GBDeviceMusic>) intent.getSerializableExtra("musicList");
|
||||
if (list != null && !list.isEmpty()) {
|
||||
allMusic.addAll(list);
|
||||
}
|
||||
|
||||
ArrayList<GBDeviceMusicPlaylist> devicePlaylist = (ArrayList<GBDeviceMusicPlaylist>) intent.getSerializableExtra("musicPlaylist");
|
||||
if (devicePlaylist != null && !devicePlaylist.isEmpty()) {
|
||||
playlists.addAll(devicePlaylist);
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void musicOperationResponse(Intent intent) {
|
||||
int operation = intent.getIntExtra("operation", -1);
|
||||
if (operation == 0) {
|
||||
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
|
||||
String playlistName = intent.getStringExtra("playlistName");
|
||||
|
||||
if (playlistIndex != -1 && !TextUtils.isEmpty(playlistName)) {
|
||||
playlists.add(new GBDeviceMusicPlaylist(playlistIndex, playlistName, new ArrayList<>()));
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (operation == 1) {
|
||||
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
|
||||
if (playlistIndex != -1) {
|
||||
for (Iterator<GBDeviceMusicPlaylist> iterator = playlists.iterator(); iterator.hasNext(); ) {
|
||||
GBDeviceMusicPlaylist playlist = iterator.next();
|
||||
if (playlist.getId() == playlistIndex) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (operation == 2) {
|
||||
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
|
||||
String playlistName = intent.getStringExtra("playlistName");
|
||||
if (playlistIndex != -1 && !TextUtils.isEmpty(playlistName)) {
|
||||
for (GBDeviceMusicPlaylist playlist : playlists) {
|
||||
if (playlist.getId() == playlistIndex) {
|
||||
playlist.setName(playlistName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
}
|
||||
} else if (operation == 3) {
|
||||
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
|
||||
ArrayList<Integer> ids = (ArrayList<Integer>) intent.getSerializableExtra("musicIds");
|
||||
if (playlistIndex != -1 && ids != null && !ids.isEmpty()) {
|
||||
for (GBDeviceMusicPlaylist playlist : playlists) {
|
||||
if (playlist.getId() == playlistIndex) {
|
||||
ArrayList<Integer> currentList = playlist.getMusicIds();
|
||||
for (Integer id : ids) {
|
||||
if (!currentList.contains(id))
|
||||
currentList.add(id);
|
||||
}
|
||||
playlist.setMusicIds(currentList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
updateCurrentMusicList();
|
||||
}
|
||||
|
||||
} else if (operation == 4) {
|
||||
ArrayList<Integer> ids = (ArrayList<Integer>) intent.getSerializableExtra("musicIds");
|
||||
int playlistIndex = intent.getIntExtra("playlistIndex", 0);
|
||||
if (ids != null && !ids.isEmpty()) {
|
||||
if (playlistIndex == 0) {
|
||||
for (Iterator<GBDeviceMusic> iterator = musicList.iterator(); iterator.hasNext(); ) {
|
||||
GBDeviceMusic music = iterator.next();
|
||||
if (ids.contains(music.getId())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
for (Iterator<GBDeviceMusic> iterator = allMusic.iterator(); iterator.hasNext(); ) {
|
||||
GBDeviceMusic music = iterator.next();
|
||||
if (ids.contains(music.getId())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (GBDeviceMusicPlaylist playlist : playlists) {
|
||||
if (playlist.getId() == playlistIndex) {
|
||||
ArrayList<Integer> currentList = playlist.getMusicIds();
|
||||
for (Integer id : ids) {
|
||||
currentList.remove(id);
|
||||
}
|
||||
playlist.setMusicIds(currentList);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
updateCurrentMusicList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
case ACTION_MUSIC_DATA: {
|
||||
if (!intent.hasExtra("type"))
|
||||
break;
|
||||
int type = intent.getIntExtra("type", -1);
|
||||
|
||||
LOG.info("UPDATE type: {}", type);
|
||||
if (type == 1) {
|
||||
startSyncFromDevice(intent);
|
||||
} else if (type == 2) {
|
||||
LOG.info("got music list or playlist from device");
|
||||
musicListFromDevice(intent);
|
||||
} else if (type == 10) {
|
||||
updateCurrentMusicList();
|
||||
stopLoading();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_MUSIC_UPDATE: {
|
||||
boolean success = intent.getBooleanExtra("success", false);
|
||||
if (intent.hasExtra("operation") && success) {
|
||||
musicOperationResponse(intent);
|
||||
}
|
||||
stopLoading();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
|
||||
public class MusicListAdapter extends RecyclerView.Adapter<MusicListAdapter.MusicViewHolder> {
|
||||
|
||||
public interface onItemAction {
|
||||
void onItemClick(View view, GBDeviceMusic music);
|
||||
boolean onItemLongClick(View view, GBDeviceMusic music);
|
||||
}
|
||||
|
||||
private final List<GBDeviceMusic> musicList;
|
||||
private final onItemAction callback;
|
||||
|
||||
public MusicListAdapter(List<GBDeviceMusic> list, onItemAction callback) {
|
||||
this.musicList = list;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return musicList.get(position).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return musicList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MusicListAdapter.MusicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_musicmanager_song, parent, false);
|
||||
return new MusicViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final MusicListAdapter.MusicViewHolder holder, int position) {
|
||||
final GBDeviceMusic music = musicList.get(position);
|
||||
|
||||
holder.musicTitle.setText(music.getTitle());
|
||||
holder.musicArtist.setText(music.getArtist());
|
||||
|
||||
if(callback != null) {
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
callback.onItemClick(view, music);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
return callback.onItemLongClick(view, music);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MusicViewHolder extends RecyclerView.ViewHolder {
|
||||
final TextView musicArtist;
|
||||
final TextView musicTitle;
|
||||
|
||||
MusicViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
musicArtist = itemView.findViewById(R.id.item_details);
|
||||
musicTitle = itemView.findViewById(R.id.item_name);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusicPlaylist;
|
||||
|
||||
public class GBDeviceMusicData extends GBDeviceEvent {
|
||||
public int type = 0; // 1 - sync start, 2 - music list, 10 - end sync
|
||||
public List<GBDeviceMusic> list = null;
|
||||
public List<GBDeviceMusicPlaylist> playlists = null;
|
||||
public String deviceInfo = null;
|
||||
public int maxMusicCount = 0;
|
||||
public int maxPlaylistCount = 0;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GBDeviceMusicUpdate extends GBDeviceEvent {
|
||||
public boolean success = false;
|
||||
public int operation = -1;
|
||||
public int playlistIndex = -1;
|
||||
public String playlistName;
|
||||
public ArrayList<Integer> musicIds = null;
|
||||
}
|
@ -95,7 +95,7 @@ public interface EventHandler {
|
||||
|
||||
void onAppConfiguration(UUID appUuid, String config, Integer id);
|
||||
|
||||
void onAppReorder(UUID uuids[]);
|
||||
void onAppReorder(UUID[] uuids);
|
||||
|
||||
void onFetchRecordedData(int dataTypes);
|
||||
|
||||
@ -154,4 +154,9 @@ public interface EventHandler {
|
||||
void onSleepAsAndroidAction(String action, Bundle extras);
|
||||
|
||||
void onCameraStatusChange(GBDeviceEventCameraRemote.Event event, String filename);
|
||||
|
||||
|
||||
void onMusicListReq();
|
||||
|
||||
void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds);
|
||||
}
|
||||
|
@ -305,6 +305,11 @@ public class HuaweiCoordinator {
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_contacts);
|
||||
}
|
||||
|
||||
//Music
|
||||
if (supportsMusicUploading() && getMusicInfoParams() != null && device.isConnected()) {
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_musicmanagement);
|
||||
}
|
||||
|
||||
// Time
|
||||
if (supportsDateFormat()) {
|
||||
final List<Integer> dateTime = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DATE_TIME);
|
||||
@ -602,8 +607,6 @@ public class HuaweiCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean supportsCalendar() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(171) || supportsExpandCapability(184);
|
||||
@ -628,6 +631,12 @@ public class HuaweiCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsMoreMusic() {
|
||||
if (supportsExpandCapability())
|
||||
return supportsExpandCapability(122);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public boolean supportsPromptPushMessage () {
|
||||
// do not ask for capabilities under specific condition
|
||||
|
@ -7,16 +7,16 @@ import java.util.List;
|
||||
public class HuaweiMusicUtils {
|
||||
|
||||
public static class PageStruct {
|
||||
public short startIndex = 0;
|
||||
public short endIndex = 0;
|
||||
public short startFrame = 0;
|
||||
public short endFrame = 0;
|
||||
public short count = 0;
|
||||
public byte[] hashCode = null;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("PageStruct{");
|
||||
sb.append("startIndex=").append(startIndex);
|
||||
sb.append(", endIndex=").append(endIndex);
|
||||
sb.append("startFrame=").append(startFrame);
|
||||
sb.append(", endFrame=").append(endFrame);
|
||||
sb.append(", count=").append(count);
|
||||
sb.append(", hashCode=");
|
||||
if (hashCode == null) sb.append("null");
|
||||
@ -68,7 +68,6 @@ public class HuaweiMusicUtils {
|
||||
public int currentMusicCount = 0; // TODO: not sure
|
||||
public int unknown = 0; // TODO: not sure
|
||||
public List<FormatRestrictions> formatsRestrictions = null;
|
||||
public List<PageStruct> pageStruct = null;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -80,7 +79,6 @@ public class HuaweiMusicUtils {
|
||||
sb.append(", currentMusicCount=").append(currentMusicCount);
|
||||
sb.append(", unknown=").append(unknown);
|
||||
sb.append(", formatsRestrictions=").append(formatsRestrictions);
|
||||
sb.append(", pageStruct=").append(pageStruct);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -598,6 +598,14 @@ public class HuaweiPacket {
|
||||
return new MusicControl.Control.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.MusicInfoParams.id:
|
||||
return new MusicControl.MusicInfoParams.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.MusicList.id:
|
||||
return new MusicControl.MusicList.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.MusicPlaylists.id:
|
||||
return new MusicControl.MusicPlaylists.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.MusicPlaylistMusics.id:
|
||||
return new MusicControl.MusicPlaylistMusics.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.MusicOperation.id:
|
||||
return new MusicControl.MusicOperation.Response(paramsProvider).fromPacket(this);
|
||||
case MusicControl.UploadMusicFileInfo.id:
|
||||
return new MusicControl.UploadMusicFileInfo.UploadMusicFileInfoRequest(paramsProvider).fromPacket(this);
|
||||
case MusicControl.ExtendedMusicInfoParams.id:
|
||||
|
@ -18,12 +18,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiMusicUtils.parseFormatBits;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiMusicUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
|
||||
public class MusicControl {
|
||||
public static final byte id = 0x25;
|
||||
@ -251,16 +253,16 @@ public class MusicControl {
|
||||
public static class Response extends HuaweiPacket {
|
||||
public HuaweiMusicUtils.MusicCapabilities params = new HuaweiMusicUtils.MusicCapabilities();
|
||||
|
||||
public int frameCount = 0;
|
||||
public List<HuaweiMusicUtils.PageStruct> pageStruct = null;
|
||||
|
||||
public Response(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws ParseException {
|
||||
|
||||
//TODO: unknown TLV
|
||||
// if (this.tlv.contains(0x01))
|
||||
// LOG.info("Unknown: " + this.tlv.getShort(0x01));
|
||||
this.frameCount = this.tlv.getAsInteger(0x01);
|
||||
|
||||
if (this.tlv.contains(0x02))
|
||||
params.availableSpace = this.tlv.getAsInteger(0x02);
|
||||
@ -274,25 +276,214 @@ public class MusicControl {
|
||||
params.currentMusicCount = this.tlv.getAsInteger(0x05);
|
||||
|
||||
if (this.tlv.contains(0x86)) {
|
||||
params.pageStruct = new ArrayList<>();
|
||||
this.pageStruct = new ArrayList<>();
|
||||
List<HuaweiTLV> subTlvs = this.tlv.getObject(0x86).getObjects(0x87);
|
||||
for (HuaweiTLV subTlv : subTlvs) {
|
||||
HuaweiMusicUtils.PageStruct pageStruct = new HuaweiMusicUtils.PageStruct();
|
||||
if (subTlv.contains(0x08))
|
||||
pageStruct.startIndex = subTlv.getShort(0x08);
|
||||
pageStruct.startFrame = subTlv.getShort(0x08);
|
||||
if (subTlv.contains(0x09))
|
||||
pageStruct.endIndex = subTlv.getShort(0x09);
|
||||
pageStruct.endFrame = subTlv.getShort(0x09);
|
||||
if (subTlv.contains(0x0a))
|
||||
pageStruct.count = subTlv.getShort(0x0a);
|
||||
if (subTlv.contains(0x0b))
|
||||
pageStruct.hashCode = subTlv.getBytes(0x0b);
|
||||
params.pageStruct.add(pageStruct);
|
||||
this.pageStruct.add(pageStruct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MusicList {
|
||||
public static final byte id = 0x05;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
public Request(ParamsProvider paramsProvider, short startFrame, short endIndex) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = id;
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, startFrame)
|
||||
.put(0x04, endIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
|
||||
public short startFrame = 0;
|
||||
public short endIndex = 0;
|
||||
|
||||
public List<GBDeviceMusic> musicList;
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(tlv.contains(0x1))
|
||||
startFrame = tlv.getShort(0x1);
|
||||
if(tlv.contains(0x4))
|
||||
endIndex = tlv.getShort(0x4);
|
||||
musicList = new ArrayList<>();
|
||||
if(this.tlv.contains(0x82)) {
|
||||
for (HuaweiTLV subTlv : this.tlv.getObject(0x82).getObjects(0x83)) {
|
||||
int index = subTlv.getAsInteger(0x4);
|
||||
String title = subTlv.getString(0x5);
|
||||
String artist = subTlv.getString(0x6);
|
||||
String fileName = subTlv.getString(0x7);
|
||||
musicList.add(new GBDeviceMusic(index, title, artist, fileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MusicPlaylists {
|
||||
public static final byte id = 0x06;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
public Request(ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = id;
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
|
||||
public static class PlaylistData {
|
||||
public int id;
|
||||
public String name;
|
||||
public int frameCount;
|
||||
}
|
||||
|
||||
public List<PlaylistData> playlists = new ArrayList<>();
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x81)) {
|
||||
for (HuaweiTLV subTlv : this.tlv.getObject(0x81).getObjects(0x82)) {
|
||||
PlaylistData data = new PlaylistData();
|
||||
data.id = subTlv.getAsInteger(0x3);
|
||||
data.name = subTlv.getString(0x4);
|
||||
data.frameCount = subTlv.getAsInteger(0x5);
|
||||
playlists.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MusicPlaylistMusics {
|
||||
public static final byte id = 0x07;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
public Request(ParamsProvider paramsProvider, short playlist, short index) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = id;
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, playlist)
|
||||
.put(0x02, index);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
|
||||
public int id = -1;
|
||||
public int index = -1;
|
||||
public ArrayList<Integer> musicIds = null;
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x1))
|
||||
id = tlv.getAsInteger(0x1);
|
||||
if(this.tlv.contains(0x2))
|
||||
index = tlv.getAsInteger(0x2);
|
||||
|
||||
if(this.tlv.contains(0x3)) {
|
||||
musicIds = new ArrayList<>();
|
||||
ByteBuffer dt = ByteBuffer.wrap(this.tlv.getBytes(0x3));
|
||||
while (dt.hasRemaining())
|
||||
musicIds.add((int) dt.getShort());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MusicOperation {
|
||||
public static final byte id = 0x08;
|
||||
|
||||
public static class Request extends HuaweiPacket {
|
||||
public Request(ParamsProvider paramsProvider, int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
super(paramsProvider);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = id;
|
||||
this.tlv = new HuaweiTLV()
|
||||
.put(0x01, (byte)operation);
|
||||
|
||||
if(operation == 1 || operation == 2 || operation == 3 || operation == 4) {
|
||||
this.tlv.put(0x02, (short)playlistIndex);
|
||||
}
|
||||
if (operation == 0 || operation == 2) {
|
||||
this.tlv.put(0x03, playlistName);
|
||||
}
|
||||
|
||||
if (operation == 3 || operation == 4) {
|
||||
ByteBuffer ids = ByteBuffer.allocate(musicIds.size() * 2);
|
||||
for (Integer id : musicIds) {
|
||||
ids.putShort(id.shortValue());
|
||||
}
|
||||
this.tlv.put(0x04, ids.array());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response extends HuaweiPacket {
|
||||
|
||||
public int operation = -1;
|
||||
public int playlistIndex = -1;
|
||||
public String playlistName;
|
||||
public ArrayList<Integer> musicIds = null;
|
||||
public int resultCode = -1;
|
||||
|
||||
public Response (ParamsProvider paramsProvider) {
|
||||
super(paramsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseTlv() throws HuaweiPacket.ParseException {
|
||||
if(this.tlv.contains(0x7f))
|
||||
resultCode = tlv.getInteger(0x7f);
|
||||
if(this.tlv.contains(0x1))
|
||||
operation = tlv.getByte(0x1);
|
||||
if(this.tlv.contains(0x2))
|
||||
playlistIndex = tlv.getAsInteger(0x2);
|
||||
if(this.tlv.contains(0x3))
|
||||
playlistName = tlv.getString(0x3);
|
||||
|
||||
if(this.tlv.contains(0x4)) {
|
||||
musicIds = new ArrayList<>();
|
||||
ByteBuffer dt = ByteBuffer.wrap(this.tlv.getBytes(0x4));
|
||||
while (dt.hasRemaining())
|
||||
musicIds.add((int) dt.getShort());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ExtendedMusicInfoParams {
|
||||
public static final byte id = 0x0d;
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class GBDeviceMusic implements Serializable {
|
||||
private final int id;
|
||||
private final String title;
|
||||
private final String artist;
|
||||
private final String fileName;
|
||||
|
||||
public GBDeviceMusic(int id, String title, String artist, String fileName) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.artist = artist;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GBDeviceMusicPlaylist implements Serializable {
|
||||
private final int id;
|
||||
private String name;
|
||||
private ArrayList<Integer> musicIds;
|
||||
|
||||
public GBDeviceMusicPlaylist(int id, String name, ArrayList<Integer> musicIds) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.musicIds = musicIds;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ArrayList<Integer> getMusicIds() {
|
||||
return musicIds;
|
||||
}
|
||||
|
||||
public void setMusicIds(ArrayList<Integer> musicIds) {
|
||||
this.musicIds = musicIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -568,4 +568,20 @@ public class GBDeviceService implements DeviceService {
|
||||
intent.putExtra(EXTRA_CAMERA_FILENAME, filename);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicListReq() {
|
||||
Intent intent = createIntent().setAction(ACTION_REQUEST_MUSIC_LIST);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
Intent intent = createIntent().setAction(ACTION_REQUEST_MUSIC_OPERATION);
|
||||
intent.putExtra("operation", operation);
|
||||
intent.putExtra("playlistIndex", playlistIndex);
|
||||
intent.putExtra("playlistName", playlistName);
|
||||
intent.putExtra("musicIds", musicIds);
|
||||
invokeService(intent);
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ public interface DeviceService extends EventHandler {
|
||||
String ACTION_SET_LED_COLOR = PREFIX + ".action.set_led_color";
|
||||
String ACTION_POWER_OFF = PREFIX + ".action.power_off";
|
||||
String ACTION_CAMERA_STATUS_CHANGE = PREFIX + ".action.camera_status_change";
|
||||
String ACTION_REQUEST_MUSIC_LIST = PREFIX + ".action.request_music_list";
|
||||
String ACTION_REQUEST_MUSIC_OPERATION = PREFIX + ".action.request_music_operation";
|
||||
|
||||
String ACTION_SLEEP_AS_ANDROID = ".action.sleep_as_android";
|
||||
String EXTRA_SLEEP_AS_ANDROID_ACTION = "sleepasandroid_action";
|
||||
|
@ -63,6 +63,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.CameraActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.FindPhoneActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AbstractAppManagerFragment;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.musicmanager.MusicManagerActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.loyaltycards.LoyaltyCard;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
@ -86,12 +87,16 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePref
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventWearState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceMusicData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceMusicUpdate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BatteryLevel;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.opentracks.OpenTracksController;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusicPlaylist;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
@ -236,7 +241,12 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
handleGBDeviceEvent((GBDeviceEventWearState) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceEventSleepStateDetection) {
|
||||
handleGBDeviceEvent((GBDeviceEventSleepStateDetection) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceMusicData) {
|
||||
handleGBDeviceEvent((GBDeviceMusicData) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceMusicUpdate) {
|
||||
handleGBDeviceEvent((GBDeviceMusicUpdate) deviceEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleGBDeviceEvent(GBDeviceEventSilentMode deviceEvent) {
|
||||
@ -751,6 +761,53 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
handleDeviceAction(actionOnUnwear, broadcastMessage);
|
||||
}
|
||||
|
||||
private void handleGBDeviceEvent(GBDeviceMusicData deviceEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for ACTION_MUSIC_DATA");
|
||||
|
||||
Intent intent = new Intent(MusicManagerActivity.ACTION_MUSIC_DATA);
|
||||
|
||||
intent.putExtra("type", deviceEvent.type);
|
||||
|
||||
if(deviceEvent.list != null) {
|
||||
ArrayList<GBDeviceMusic> list = new ArrayList<>(deviceEvent.list);
|
||||
intent.putExtra("musicList", list);
|
||||
}
|
||||
|
||||
if(deviceEvent.playlists != null) {
|
||||
ArrayList<GBDeviceMusicPlaylist> list = new ArrayList<>(deviceEvent.playlists);
|
||||
intent.putExtra("musicPlaylist", list);
|
||||
}
|
||||
|
||||
if(!TextUtils.isEmpty(deviceEvent.deviceInfo)) {
|
||||
intent.putExtra("deviceInfo", deviceEvent.deviceInfo);
|
||||
}
|
||||
|
||||
if(deviceEvent.maxMusicCount > 0) {
|
||||
intent.putExtra("maxMusicCount", deviceEvent.maxMusicCount);
|
||||
}
|
||||
if(deviceEvent.maxPlaylistCount > 0) {
|
||||
intent.putExtra("maxPlaylistCount", deviceEvent.maxPlaylistCount);
|
||||
}
|
||||
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private void handleGBDeviceEvent(GBDeviceMusicUpdate deviceEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for ACTION_MUSIC_UPDATE");
|
||||
|
||||
Intent intent = new Intent(MusicManagerActivity.ACTION_MUSIC_UPDATE);
|
||||
|
||||
intent.putExtra("success", deviceEvent.success);
|
||||
intent.putExtra("operation", deviceEvent.operation);
|
||||
intent.putExtra("playlistIndex", deviceEvent.playlistIndex);
|
||||
intent.putExtra("playlistName", deviceEvent.playlistName);
|
||||
intent.putExtra("musicIds", deviceEvent.musicIds);
|
||||
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private StoreDataTask createStoreTask(String task, Context context, GBDeviceEventBatteryInfo deviceEvent) {
|
||||
return new StoreDataTask(task, context, deviceEvent);
|
||||
}
|
||||
@ -1233,4 +1290,10 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onCameraStatusChange(GBDeviceEventCameraRemote.Event event, String filename) {}
|
||||
|
||||
@Override
|
||||
public void onMusicListReq() {}
|
||||
|
||||
@Override
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.TinyWeatherForecastGe
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.sleepasandroid.SleepAsAndroidReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
@ -1137,6 +1138,16 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
}
|
||||
deviceSupport.onCameraStatusChange(event, filename);
|
||||
break;
|
||||
case ACTION_REQUEST_MUSIC_LIST:
|
||||
deviceSupport.onMusicListReq();
|
||||
break;
|
||||
case ACTION_REQUEST_MUSIC_OPERATION:
|
||||
int operation = intentCopy.getIntExtra("operation", -1);
|
||||
int playlistIndex = intentCopy.getIntExtra("playlistIndex", -1);
|
||||
String playlistName = intentCopy.getStringExtra("playlistName");
|
||||
ArrayList<Integer> musics = (ArrayList<Integer>) intentCopy.getSerializableExtra("musicIds");
|
||||
deviceSupport.onMusicOperation(operation, playlistIndex, playlistName, musics);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ import java.util.UUID;
|
||||
import nodomain.freeyourgadget.gadgetbridge.capabilities.loyaltycards.LoyaltyCard;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
@ -524,4 +525,20 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||
}
|
||||
delegate.onCameraStatusChange(event, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicListReq() {
|
||||
if (checkBusy("music list request")) {
|
||||
return;
|
||||
}
|
||||
delegate.onMusicListReq();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
if (checkBusy("music operation")) {
|
||||
return;
|
||||
}
|
||||
delegate.onMusicOperation(operation, playlistIndex, playlistName, musicIds);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import java.util.UUID;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
||||
@ -198,4 +199,14 @@ public class HuaweiBRSupport extends AbstractBTBRDeviceSupport {
|
||||
public void onTestNewFunction() {
|
||||
supportProvider.onTestNewFunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicListReq() {
|
||||
supportProvider.onMusicListReq();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
supportProvider.onMusicOperation(operation, playlistIndex, playlistName, musicIds);
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import java.util.UUID;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Contact;
|
||||
@ -214,4 +215,14 @@ public class HuaweiLESupport extends AbstractBTLEDeviceSupport {
|
||||
public boolean getSendWriteRequestResponse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicListReq() {
|
||||
supportProvider.onMusicListReq();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
supportProvider.onMusicOperation(operation, playlistIndex, playlistName, musicIds);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,28 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceMusicData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceMusicUpdate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiMusicUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusicPlaylist;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicInfoParams;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicList;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicPlaylist;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.GetMusicPlaylistMusics;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendMusicOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests.SendUploadMusicFileInfoResponse;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class HuaweiMusicManager {
|
||||
static Logger LOG = LoggerFactory.getLogger(HuaweiMusicManager.class);
|
||||
@ -125,4 +142,236 @@ public class HuaweiMusicManager {
|
||||
LOG.error("Could not send sendUploadMusicFileInfoResponse", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean syncMusicData = false;
|
||||
private int frameCount = 0;
|
||||
private int endFrame = 65535;
|
||||
private int currentFrame = 0;
|
||||
|
||||
|
||||
public void startSyncMusicData() {
|
||||
syncMusicData = true;
|
||||
try {
|
||||
GetMusicInfoParams getMusicInfoParams = new GetMusicInfoParams(this.support);
|
||||
getMusicInfoParams.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Get music info: {}", e.getMessage());
|
||||
syncMusicData = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void syncMusicList() {
|
||||
if (!syncMusicData) {
|
||||
this.currentFrame = 0;
|
||||
return;
|
||||
}
|
||||
int count = this.frameCount;
|
||||
if (support.getHuaweiCoordinator().supportsMoreMusic()) {
|
||||
count = Math.min(this.frameCount, 250);
|
||||
}
|
||||
if (this.currentFrame < count) {
|
||||
try {
|
||||
GetMusicList getMusicList = new GetMusicList(this.support, this.currentFrame, this.endFrame);
|
||||
getMusicList.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Get music list: {}", e.getMessage());
|
||||
endMusicListSync();
|
||||
}
|
||||
} else {
|
||||
endMusicListSync();
|
||||
}
|
||||
}
|
||||
|
||||
private void endMusicListSync() {
|
||||
this.currentFrame = 0;
|
||||
try {
|
||||
GetMusicPlaylist getMusicPlaylist = new GetMusicPlaylist(this.support);
|
||||
getMusicPlaylist.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Get music playlist: {}", e.getMessage());
|
||||
endMusicPlaylistSync();
|
||||
}
|
||||
}
|
||||
|
||||
private void endMusicPlaylistSync() {
|
||||
this.currentPlaylistIndex = 0;
|
||||
this.currentPlaylistFrame = 0;
|
||||
tempPlaylistMusic.clear();
|
||||
|
||||
musicPlaylistMusicSync();
|
||||
}
|
||||
|
||||
private final List<MusicControl.MusicPlaylists.Response.PlaylistData> devicePlaylists = new ArrayList<>();
|
||||
|
||||
private int currentPlaylistIndex = 0;
|
||||
private int currentPlaylistFrame = 0;
|
||||
private final List<List<Integer>> tempPlaylistMusic = new ArrayList<>();
|
||||
|
||||
private void musicPlaylistMusicSync() {
|
||||
if (this.currentPlaylistIndex < devicePlaylists.size()) {
|
||||
MusicControl.MusicPlaylists.Response.PlaylistData playlist = devicePlaylists.get(this.currentPlaylistIndex);
|
||||
syncPlaylistMusicsOne(playlist.id, playlist.frameCount);
|
||||
} else {
|
||||
musicPlaylistMusicDone();
|
||||
}
|
||||
}
|
||||
|
||||
private void syncPlaylistMusicsOne(int id, int frameCount) {
|
||||
if (this.currentPlaylistFrame < frameCount) {
|
||||
try {
|
||||
GetMusicPlaylistMusics getMusicPlaylistMusics = new GetMusicPlaylistMusics(this.support, id, this.currentPlaylistFrame);
|
||||
getMusicPlaylistMusics.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Get music playlist musics: {}", e.getMessage());
|
||||
musicPlaylistMusicDone();
|
||||
}
|
||||
} else {
|
||||
syncPlayListMusicIndexDone(id, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void syncNextPlaylistMusicIndex() {
|
||||
this.currentPlaylistFrame++;
|
||||
MusicControl.MusicPlaylists.Response.PlaylistData playlist = devicePlaylists.get(this.currentPlaylistIndex);
|
||||
syncPlaylistMusicsOne(playlist.id, playlist.frameCount);
|
||||
}
|
||||
|
||||
private void syncPlayListMusicIndexDone(int id, int frameCount) {
|
||||
MusicControl.MusicPlaylists.Response.PlaylistData playlist = devicePlaylists.get(this.currentPlaylistIndex);
|
||||
|
||||
ArrayList<Integer> musics = new ArrayList<>();
|
||||
if (this.tempPlaylistMusic.size() == frameCount) {
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
musics.addAll(this.tempPlaylistMusic.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
GBDeviceMusicPlaylist pl = new GBDeviceMusicPlaylist(playlist.id, playlist.name, musics);
|
||||
List<GBDeviceMusicPlaylist> list = new ArrayList<>();
|
||||
list.add(pl);
|
||||
sendMusicPlaylist(list);
|
||||
this.currentPlaylistIndex++;
|
||||
this.currentPlaylistFrame = 0;
|
||||
this.tempPlaylistMusic.clear();
|
||||
musicPlaylistMusicSync();
|
||||
}
|
||||
|
||||
private void musicPlaylistMusicDone() {
|
||||
this.currentPlaylistIndex = 0;
|
||||
this.currentPlaylistFrame = 0;
|
||||
this.tempPlaylistMusic.clear();
|
||||
|
||||
this.syncMusicData = false;
|
||||
sendMusicSyncDone();
|
||||
}
|
||||
|
||||
public void onMusicMusicInfoParams(HuaweiMusicUtils.MusicCapabilities capabilities, int frameCount, List<HuaweiMusicUtils.PageStruct> pageStruct) {
|
||||
//TODO: research and use pageStruct. It may/should be used to retrieve music data from devices by pages.
|
||||
// without it list can be incomplete, but I can't confirm this.
|
||||
LOG.info("FrameCount: {}, pageStruct: {}", frameCount, pageStruct);
|
||||
support.getHuaweiCoordinator().setMusicInfoParams(capabilities);
|
||||
if(syncMusicData) {
|
||||
this.frameCount = frameCount;
|
||||
this.currentFrame = 0;
|
||||
this.endFrame = 65535;
|
||||
String formats = null;
|
||||
if(capabilities.supportedFormats != null) {
|
||||
formats = String.join(",", capabilities.supportedFormats);
|
||||
}
|
||||
int maxPlaylistCount = 0;
|
||||
if(support.getCoordinator().getHuaweiCoordinator().getExtendedMusicInfoParams() != null) {
|
||||
maxPlaylistCount = support.getCoordinator().getHuaweiCoordinator().getExtendedMusicInfoParams().maxPlaylistCount;
|
||||
}
|
||||
sendMusicSyncStart(support.getContext().getString(R.string.music_huawei_device_info, formats, capabilities.availableSpace), capabilities.maxMusicCount, maxPlaylistCount);
|
||||
syncMusicList();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMusicSyncStart(final String info, int maxMusicCount, int maxPlaylistCount) {
|
||||
final GBDeviceMusicData musicListCmd = new GBDeviceMusicData();
|
||||
musicListCmd.type = 1;
|
||||
musicListCmd.deviceInfo = info;
|
||||
musicListCmd.maxMusicCount = maxMusicCount;
|
||||
musicListCmd.maxPlaylistCount = maxPlaylistCount;
|
||||
support.evaluateGBDeviceEvent(musicListCmd);
|
||||
}
|
||||
|
||||
|
||||
private void sendMusicList(List<GBDeviceMusic> list) {
|
||||
final GBDeviceMusicData musicListCmd = new GBDeviceMusicData();
|
||||
musicListCmd.type = 2;
|
||||
musicListCmd.list = list;
|
||||
support.evaluateGBDeviceEvent(musicListCmd);
|
||||
}
|
||||
|
||||
private void sendMusicPlaylist(List<GBDeviceMusicPlaylist> list) {
|
||||
final GBDeviceMusicData musicListCmd = new GBDeviceMusicData();
|
||||
musicListCmd.type = 2;
|
||||
musicListCmd.playlists = list;
|
||||
support.evaluateGBDeviceEvent(musicListCmd);
|
||||
}
|
||||
|
||||
private void sendMusicSyncDone() {
|
||||
final GBDeviceMusicData musicListCmd = new GBDeviceMusicData();
|
||||
musicListCmd.type = 10;
|
||||
support.evaluateGBDeviceEvent(musicListCmd);
|
||||
}
|
||||
|
||||
public void onMusicListResponse(int startFrame, int endFrame, List<GBDeviceMusic> list) {
|
||||
sendMusicList(list);
|
||||
if (support.getHuaweiCoordinator().supportsMoreMusic() || !(endFrame == this.endFrame || list.size() == 1)) {
|
||||
if (list.size() == 2) {
|
||||
this.endFrame = list.get(1).getId();
|
||||
}
|
||||
this.currentFrame++;
|
||||
syncMusicList();
|
||||
return;
|
||||
}
|
||||
endMusicListSync();
|
||||
}
|
||||
|
||||
public void onMusicPlaylistResponse(List<MusicControl.MusicPlaylists.Response.PlaylistData> playlists) {
|
||||
this.devicePlaylists.clear();
|
||||
for(MusicControl.MusicPlaylists.Response.PlaylistData pl: playlists) {
|
||||
if(pl.id != 0) {
|
||||
this.devicePlaylists.add(pl);
|
||||
}
|
||||
}
|
||||
endMusicPlaylistSync();
|
||||
}
|
||||
|
||||
public void onMusicPlaylistMusics(int id, int index, List<Integer> musicIds) {
|
||||
this.tempPlaylistMusic.add(musicIds);
|
||||
syncNextPlaylistMusicIndex();
|
||||
}
|
||||
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
LOG.info("music operation: {}", operation);
|
||||
try {
|
||||
SendMusicOperation sendMusicOperation = new SendMusicOperation(this.support, operation, playlistIndex, playlistName, musicIds);
|
||||
sendMusicOperation.doPerform();
|
||||
} catch (IOException e) {
|
||||
LOG.error("SendMusicOperation: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void onMusicOperationResponse(int resultCode, int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
|
||||
boolean success = true;
|
||||
if (resultCode != 0x000186A0) {
|
||||
GB.toast(support.getContext(), support.getContext().getString(R.string.music_error), Toast.LENGTH_SHORT, GB.ERROR);
|
||||
success = false;
|
||||
}
|
||||
|
||||
LOG.info("music operation response: {} {}", operation, success);
|
||||
final GBDeviceMusicUpdate updateCmd = new GBDeviceMusicUpdate();
|
||||
updateCmd.success = success;
|
||||
updateCmd.operation = operation;
|
||||
updateCmd.playlistIndex = playlistIndex;
|
||||
updateCmd.playlistName = playlistName;
|
||||
updateCmd.musicIds = musicIds;
|
||||
|
||||
support.evaluateGBDeviceEvent(updateCmd);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceMusicData;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiCoordinator;
|
||||
@ -2529,4 +2530,12 @@ public class HuaweiSupportProvider {
|
||||
callback
|
||||
), true);
|
||||
}
|
||||
|
||||
public void onMusicListReq() {
|
||||
getHuaweiMusicManager().startSyncMusicData();
|
||||
}
|
||||
|
||||
public void onMusicOperation(int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
getHuaweiMusicManager().onMusicOperation(operation, playlistIndex, playlistName, musicIds);
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,6 @@ public class GetMusicInfoParams extends Request {
|
||||
throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicInfoParams.Response.class);
|
||||
|
||||
MusicControl.MusicInfoParams.Response resp = (MusicControl.MusicInfoParams.Response)(receivedPacket);
|
||||
supportProvider.getHuaweiCoordinator().setMusicInfoParams(resp.params);
|
||||
supportProvider.getHuaweiMusicManager().onMusicMusicInfoParams(resp.params, resp.frameCount, resp.pageStruct);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class GetMusicList extends Request {
|
||||
private final Logger LOG = LoggerFactory.getLogger(GetMusicList.class);
|
||||
|
||||
private final int startFrame;
|
||||
private final int endFrame;
|
||||
|
||||
public GetMusicList(HuaweiSupportProvider support, int startFrame, int endFrame) {
|
||||
super(support);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = MusicControl.MusicList.id;
|
||||
this.startFrame = startFrame;
|
||||
this.endFrame = endFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws Request.RequestCreationException {
|
||||
try {
|
||||
return new MusicControl.MusicList.Request(paramsProvider, (short) this.startFrame, (short) this.endFrame).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new Request.RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws Request.ResponseParseException {
|
||||
LOG.info("MusicControl.MusicList processResponse");
|
||||
if (!(receivedPacket instanceof MusicControl.MusicList.Response))
|
||||
throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicList.Response.class);
|
||||
|
||||
MusicControl.MusicList.Response resp = (MusicControl.MusicList.Response) (receivedPacket);
|
||||
supportProvider.getHuaweiMusicManager().onMusicListResponse(resp.startFrame, resp.endIndex, resp.musicList);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class GetMusicPlaylist extends Request {
|
||||
private final Logger LOG = LoggerFactory.getLogger(GetMusicPlaylist.class);
|
||||
|
||||
public GetMusicPlaylist(HuaweiSupportProvider support) {
|
||||
super(support);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = MusicControl.MusicPlaylists.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws Request.RequestCreationException {
|
||||
try {
|
||||
return new MusicControl.MusicPlaylists.Request(paramsProvider).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new Request.RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws Request.ResponseParseException {
|
||||
LOG.info("MusicControl.MusicPlaylists processResponse");
|
||||
if (!(receivedPacket instanceof MusicControl.MusicPlaylists.Response))
|
||||
throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicPlaylists.Response.class);
|
||||
|
||||
MusicControl.MusicPlaylists.Response resp = (MusicControl.MusicPlaylists.Response) (receivedPacket);
|
||||
supportProvider.getHuaweiMusicManager().onMusicPlaylistResponse(resp.playlists);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class GetMusicPlaylistMusics extends Request {
|
||||
private final Logger LOG = LoggerFactory.getLogger(GetMusicPlaylistMusics.class);
|
||||
|
||||
private final int playlist;
|
||||
private final int index;
|
||||
|
||||
public GetMusicPlaylistMusics(HuaweiSupportProvider support, int playlist, int index) {
|
||||
super(support);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = MusicControl.MusicPlaylistMusics.id;
|
||||
this.playlist = playlist;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws Request.RequestCreationException {
|
||||
try {
|
||||
return new MusicControl.MusicPlaylistMusics.Request(paramsProvider, (short) playlist, (short) index).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new Request.RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws Request.ResponseParseException {
|
||||
LOG.info("MusicControl.GetMusicPlaylistMusics processResponse");
|
||||
if (!(receivedPacket instanceof MusicControl.MusicPlaylistMusics.Response))
|
||||
throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicPlaylistMusics.Response.class);
|
||||
|
||||
MusicControl.MusicPlaylistMusics.Response resp = (MusicControl.MusicPlaylistMusics.Response) (receivedPacket);
|
||||
supportProvider.getHuaweiMusicManager().onMusicPlaylistMusics(resp.id, resp.index, resp.musicIds);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.requests;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.MusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huawei.HuaweiSupportProvider;
|
||||
|
||||
public class SendMusicOperation extends Request {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SendMusicOperation.class);
|
||||
|
||||
private final int operation;
|
||||
private final int playlistIndex;
|
||||
private final String playlistName;
|
||||
private final ArrayList<Integer> musicIds;
|
||||
|
||||
|
||||
public SendMusicOperation(HuaweiSupportProvider support, int operation, int playlistIndex, String playlistName, ArrayList<Integer> musicIds) {
|
||||
super(support);
|
||||
this.serviceId = MusicControl.id;
|
||||
this.commandId = MusicControl.MusicOperation.id;
|
||||
this.operation = operation;
|
||||
this.playlistIndex = playlistIndex;
|
||||
this.playlistName = playlistName;
|
||||
this.musicIds = musicIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<byte[]> createRequest() throws Request.RequestCreationException {
|
||||
try {
|
||||
return new MusicControl.MusicOperation.Request(paramsProvider, operation, playlistIndex, playlistName, musicIds).serialize();
|
||||
} catch (HuaweiPacket.CryptoException e) {
|
||||
throw new Request.RequestCreationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processResponse() throws ResponseTypeMismatchException {
|
||||
LOG.debug("handle Music Operation");
|
||||
if (!(receivedPacket instanceof MusicControl.MusicOperation.Response))
|
||||
throw new Request.ResponseTypeMismatchException(receivedPacket, MusicControl.MusicOperation.Response.class);
|
||||
|
||||
MusicControl.MusicOperation.Response resp = (MusicControl.MusicOperation.Response) (receivedPacket);
|
||||
supportProvider.getHuaweiMusicManager().onMusicOperationResponse(resp.resultCode, resp.operation, resp.playlistIndex, resp.playlistName, resp.musicIds);
|
||||
}
|
||||
|
||||
}
|
92
app/src/main/res/layout/activity_musicmanager.xml
Normal file
92
app/src/main/res/layout/activity_musicmanager.xml
Normal file
@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.widget.RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activities.musicmanager.MusicManagerActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/music_device_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:text="" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/music_playlists_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/music_device_info"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/music_playlists"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="20" />
|
||||
<ImageButton
|
||||
android:id="@+id/music_playlist_rename"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/music_rename_playlist"
|
||||
app:srcCompat="@drawable/ic_edit"/>
|
||||
<ImageButton
|
||||
android:id="@+id/music_playlist_delete"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@null"
|
||||
android:contentDescription="@string/music_delete"
|
||||
app:srcCompat="@drawable/ic_delete" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/music_songs_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/music_playlists_layout"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:divider="@null" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_music_upload"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="@string/music_new_playlist"
|
||||
app:srcCompat="@android:drawable/stat_sys_upload" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_music_playlist_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="90dp"
|
||||
app:srcCompat="@drawable/ic_add"
|
||||
/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/music_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#95000000"
|
||||
android:gravity="center">
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</RelativeLayout>
|
||||
|
||||
</android.widget.RelativeLayout>
|
63
app/src/main/res/layout/item_musicmanager_song.xml
Normal file
63
app/src/main/res/layout/item_musicmanager_song.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/appmanager_item_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
app:cardBackgroundColor="?attr/cardview_background_color"
|
||||
app:cardElevation="3dp"
|
||||
app:contentPadding="8dp">
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/activatedBackgroundIndicator"
|
||||
android:minHeight="60dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/item_image"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:contentDescription="@string/candidate_item_device_image"
|
||||
android:src="@drawable/ic_music"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_toEndOf="@+id/item_image"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="Item Name"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Item Description"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
12
app/src/main/res/menu/musicmanager_context.xml
Normal file
12
app/src/main/res/menu/musicmanager_context.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/musicmanager_add_to_playlist"
|
||||
android:title="@string/music_add_to_playlist"/>
|
||||
<item
|
||||
android:id="@+id/musicmanager_delete_from_playlist"
|
||||
android:title="@string/music_delete_from_playlist"/>
|
||||
<item
|
||||
android:id="@+id/musicmanager_delete"
|
||||
android:title="@string/music_delete"/>
|
||||
</menu>
|
@ -191,6 +191,8 @@
|
||||
<string name="open_fw_installer_connect_minimum_one_device">Please connect AT LEAST ONE device you want to send the file to.</string>
|
||||
<string name="open_fw_installer_connect_maximum_one_device">Please connect ONLY ONE device you want to send the file to.</string>
|
||||
<string name="open_fw_installer_ensure_device_connected">Make sure that the device %s is connected</string>
|
||||
<!-- Strings related to MusicManager -->
|
||||
<string name="title_activity_musicmanager">Music Manager</string>
|
||||
<!-- Strings related to Settings -->
|
||||
<string name="title_activity_settings">Settings</string>
|
||||
<string name="proprietary_app_warning">This feature requires the installation of a proprietary app</string>
|
||||
@ -3398,4 +3400,15 @@
|
||||
<string name="inactivity_warnings_minimum_steps_summary">Minimum amount of steps that need to be taken during the threshold minutes</string>
|
||||
<string name="prefs_hrv_monitoring_title">HRV monitoring</string>
|
||||
<string name="prefs_hrv_monitoring_description">Automatically monitor heart rate variability throughout the day</string>
|
||||
<string name="pref_music_management_title">Manage Music</string>
|
||||
<string name="pref_music_management_summary">Manage music on the watch</string>
|
||||
<string name="music_delete_confirm_description">Are you sure you want to delete \'%1$s\'?</string>
|
||||
<string name="music_add_to_playlist">Add to playlist</string>
|
||||
<string name="music_delete_from_playlist">Delete from playlist</string>
|
||||
<string name="music_delete">Delete song</string>
|
||||
<string name="music_all_songs">All songs</string>
|
||||
<string name="music_new_playlist">New playlist</string>
|
||||
<string name="music_rename_playlist">Rename playlist</string>
|
||||
<string name="music_error">Error occurred</string>
|
||||
<string name="music_huawei_device_info">Supported formats: %1$s\nWatch storage: %2$d MB</string>
|
||||
</resources>
|
||||
|
8
app/src/main/res/xml/devicesettings_musicmanagement.xml
Normal file
8
app/src/main/res/xml/devicesettings_musicmanagement.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<Preference
|
||||
android:icon="@drawable/ic_music_note"
|
||||
android:key="pref_music_management"
|
||||
android:summary="@string/pref_music_management_summary"
|
||||
android:title="@string/pref_music_management_title" />
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue
Block a user