Related
I have a Xamarin Forms project on visual studio 2019.
I wanted to hide the cancel button from a simple Picker but wasn't able to do so, so I have now a custom picker but don't know how to remove that button. Either that or changing the "Cancel" button for an "Ok" would be what I want.
The thing is the examples that I have seen are customizing this by using the NumberPicker class and this won't work for me as I don't want to display my list that way.
My CustomPicker class
public class CustomPicker : Picker
{
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Image), typeof(string), typeof(CustomPicker), string.Empty);
public string Image
{
get { return (string)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
My CustomPickerRenderer class on Android project
public class CustomPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
public CustomPickerRenderer(Context context) : base(context) { }
CustomPicker element;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
element = (CustomPicker)this.Element;
if (Control != null && this.Element != null && !string.IsNullOrEmpty(element.Image))
{
Control.Background = AddPickerStyles(element.Image);
Control.Gravity = GravityFlags.CenterHorizontal;
}
}
public LayerDrawable AddPickerStyles(string imagePath)
{
ShapeDrawable border = new ShapeDrawable();
border.Paint.Color = Android.Graphics.Color.Gray;
border.SetPadding(10, 10, 10, 10);
border.Paint.SetStyle(Paint.Style.Stroke);
Drawable[] layers = { border, GetDrawable(imagePath) };
LayerDrawable layerDrawable = new LayerDrawable(layers);
layerDrawable.SetLayerInset(0, 0, 0, 0, 0);
return layerDrawable;
}
private BitmapDrawable GetDrawable(string imagePath)
{
int resID = Resources.GetIdentifier(imagePath, "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, resID);
var bitmap = ((BitmapDrawable)drawable).Bitmap;
var result = new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, 70, 70, true));
result.Gravity = Android.Views.GravityFlags.Right;
return result;
}
}
My CustomPickerRenderer on iOS project
public class CustomPickerRenderer : PickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
var element = (CustomPicker)this.Element;
if (this.Control != null && this.Element != null && !string.IsNullOrEmpty(element.Image))
{
var downarrow = UIImage.FromBundle(element.Image);
Control.RightViewMode = UITextFieldViewMode.Always;
Control.RightView = new UIImageView(downarrow);
Control.TextAlignment = UITextAlignment.Center;
}
}
}
How can I achieve this? Please help and thanks.
You can set a donetextproperty in your picker and set it in the uibuttontext on ios side.like:
public class MyPicker:Picker
{
public static readonly BindableProperty DonebuttonTextProperty = BindableProperty.Create("DonebuttonText", typeof(string),
typeof(string), null);
public string DonebuttonText { get { return (string)GetValue(DonebuttonTextProperty); }
set { SetValue(DonebuttonTextProperty, value); }
}
public MyPicker()
{
}
}
}
iOSRenderer:
public class MyPickerRenderer:PickerRenderer
{
public MyPickerRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null) return;
var customPicker = e.NewElement as MyPicker;
if (Control == null)
{
SetNativeControl(new UITextField
{
RightViewMode = UITextFieldViewMode.Always,
ClearButtonMode = UITextFieldViewMode.WhileEditing
});
SetUIbutton(customPicker.DonebuttonText);
}
else
{ SetUIbutton(customPicker.DonebuttonText); }
}
public void SetUIbutton(string doneButtonText)
{
UIToolbar toolbar = new UIToolbar();
toolbar.BarStyle = UIBarStyle.Default;
toolbar.Translucent = true;
toolbar.SizeToFit();
UIBarButtonItem doneButton = new UIBarButtonItem(string.IsNullOrEmpty(doneButtonText) ? "OK" : doneButtonText,
UIBarButtonItemStyle.Done, (s, ev) => { Control.ResignFirstResponder(); });
UIBarButtonItem flexible = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
toolbar.SetItems(new UIBarButtonItem[] { doneButton, flexible }, true);
Control.InputAccessoryView = toolbar;
}
}
As for android, change text in NegativeButton like:
public class MyPickerRenderer : PickerRenderer
{
public MyPickerRenderer(Context context) : base(context)
{
}
private IElementController ElementController => Element as IElementController;
private AlertDialog _dialog;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || e.OldElement != null)
return;
Control.Click += Control_Click;
}
protected override void Dispose(bool disposing)
{
Control.Click -= Control_Click;
base.Dispose(disposing);
}
private void Control_Click(object sender, EventArgs e)
{
Picker model = Element;
var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
picker.MaxValue = model.Items.Count - 1;
picker.MinValue = 0;
picker.SetDisplayedValues(model.Items.ToArray());
picker.WrapSelectorWheel = false;
picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
builder.SetTitle(model.Title ?? "");
builder.SetNegativeButton("OK ", (s, a) =>
{
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
_dialog = null;
});
builder.SetPositiveButton("Done", (s, a) =>
{
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null)
{
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
// It is also possible for the Content of the Page to be changed when Focus is changed.
// In this case, we'll lose our Control.
Control?.ClearFocus();
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (ssender, args) =>
{
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
};
_dialog.Show();
}
}
}
I am working on a media player app. I'm using ExoPlayer library. I have a playlist of videos, and I want to download videos simultaneously. I done it by using available demo app of exoplayer library on GitHub. I want to show progress of each downloading in the UI. For this job I get help from DownloadNotificationUtil.buildProgressNotification method.
#Override
protected Notification getForegroundNotification(TaskState[] taskStates) {
float totalPercentage = 0;
int downloadTaskCount = 0;
boolean allDownloadPercentagesUnknown = true;
boolean haveDownloadedBytes = false;
boolean haveDownloadTasks = false;
boolean haveRemoveTasks = false;
Log.e(TAG,"size task state: "+taskStates.length);
for (TaskState taskState : taskStates) {
Log.e(TAG,"taskId= "+taskState.taskId);
if (taskState.state != TaskState.STATE_STARTED
&& taskState.state != TaskState.STATE_COMPLETED) {
continue;
}
if (taskState.action.isRemoveAction) {
haveRemoveTasks = true;
continue;
}
haveDownloadTasks = true;
if (taskState.downloadPercentage != C.PERCENTAGE_UNSET) {
allDownloadPercentagesUnknown = false;
totalPercentage += taskState.downloadPercentage;
}
haveDownloadedBytes |= taskState.downloadedBytes > 0;
downloadTaskCount++;
}
int progress = 0;
boolean indeterminate = true;
if (haveDownloadTasks) {
progress = (int) (totalPercentage / downloadTaskCount);
indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes;
Log.e(TAG,"notifi "+progress);
}
return DownloadNotificationUtil.buildProgressNotification(
this,
R.drawable.exo_icon_play,
DOWNLOAD_CHANNEL_ID,
null,
null,
taskStates);
}
Now,I can track the progress downloading. But I still have a problem. I can't understand which item is downloading to update it's progress bar in the UI. Is there a Identical id of each download to recognize it? For example Android Download Manager has a download ID for each downloading file. But I don't know , how to handle this problem.
This is MediaDownloadService:
public class MediaDownloadService extends DownloadService {
public static String TAG="MediaDownloadService";
private static final int FOREGROUND_NOTIFICATION_ID = 1;
public MediaDownloadService() {
super(
DOWNLOAD_NOTIFICATION_ID,
DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
DOWNLOAD_CHANNEL_ID,
R.string.download_channel_name);
}
#Override
protected DownloadManager getDownloadManager() {
return ((MyApplication) getApplication()).getDownloadManager();
}
#Nullable
#Override
protected Scheduler getScheduler() {
return null;
}
#Override
protected Notification getForegroundNotification(TaskState[] taskStates) {
float totalPercentage = 0;
int downloadTaskCount = 0;
boolean allDownloadPercentagesUnknown = true;
boolean haveDownloadedBytes = false;
boolean haveDownloadTasks = false;
boolean haveRemoveTasks = false;
for (TaskState taskState : taskStates) {
if (taskState.state != TaskState.STATE_STARTED
&& taskState.state != TaskState.STATE_COMPLETED) {
continue;
}
if (taskState.action.isRemoveAction) {
haveRemoveTasks = true;
continue;
}
haveDownloadTasks = true;
if (taskState.downloadPercentage != C.PERCENTAGE_UNSET) {
allDownloadPercentagesUnknown = false;
totalPercentage += taskState.downloadPercentage;
}
haveDownloadedBytes |= taskState.downloadedBytes > 0;
downloadTaskCount++;
}
int progress = 0;
boolean indeterminate = true;
if (haveDownloadTasks) {
progress = (int) (totalPercentage / downloadTaskCount);
indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes;
Log.e(TAG,"notifi "+progress);
sendIntent(progress);
}
return DownloadNotificationUtil.buildProgressNotification(
this,
R.drawable.exo_icon_play,
DOWNLOAD_CHANNEL_ID,
null,
null,
taskStates);
}
private void sendIntent(int progress){
Intent intent = new Intent(ConstantUtil.MESSAGE_PROGRESS);
intent.putExtra("progress",progress);
LocalBroadcastManager.getInstance(MediaDownloadService.this).sendBroadcast(intent);
}
#Override
protected void onTaskStateChanged(TaskState taskState) {
if (taskState.action.isRemoveAction) {
return;
}
Notification notification = null;
if (taskState.state == TaskState.STATE_COMPLETED) {
Log.e(TAG,"STATE_COMPLETED");
notification =
DownloadNotificationUtil.buildDownloadCompletedNotification(
/* context= */ this,
R.drawable.exo_controls_play,
DOWNLOAD_CHANNEL_ID,
/* contentIntent= */ null,
Util.fromUtf8Bytes(taskState.action.data));
} else if (taskState.state == TaskState.STATE_FAILED) {
Log.e(TAG,"STATE_FAILED");
notification =
DownloadNotificationUtil.buildDownloadFailedNotification(
/* context= */ this,
R.drawable.exo_controls_play,
DOWNLOAD_CHANNEL_ID,
/* contentIntent= */ null,
Util.fromUtf8Bytes(taskState.action.data));
}
int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + taskState.taskId;
NotificationUtil.setNotification(this, notificationId, notification);
}
}
This is DownloadTracker class:
public class DownloadTracker implements DownloadManager.Listener {
/** Listens for changes in the tracked downloads. */
public interface Listener {
/** Called when the tracked downloads changed. */
void onDownloadsChanged();
}
private static final String TAG = "DownloadTracker";
private final Context context;
private final DataSource.Factory dataSourceFactory;
private final TrackNameProvider trackNameProvider;
private final CopyOnWriteArraySet<Listener> listeners;
private Listener onDownloadsChanged;
private final HashMap<Uri, DownloadAction> trackedDownloadStates;
private final ActionFile actionFile;
private final Handler actionFileWriteHandler;
public DownloadTracker(
Context context,
DataSource.Factory dataSourceFactory,
File actionFile,
DownloadAction.Deserializer... deserializers) {
this.context = context.getApplicationContext();
this.dataSourceFactory = dataSourceFactory;
this.actionFile = new ActionFile(actionFile);
trackNameProvider = new DefaultTrackNameProvider(context.getResources());
listeners = new CopyOnWriteArraySet<>();
trackedDownloadStates = new HashMap<>();
HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker");
actionFileWriteThread.start();
actionFileWriteHandler = new Handler(actionFileWriteThread.getLooper());
loadTrackedActions(
deserializers.length > 0 ? deserializers : DownloadAction.getDefaultDeserializers());
}
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
public boolean isDownloaded(Uri uri) {
return trackedDownloadStates.containsKey(uri);
}
#SuppressWarnings("unchecked")
public List<StreamKey> getOfflineStreamKeys(Uri uri) {
if (!trackedDownloadStates.containsKey(uri)) {
return Collections.emptyList();
}
return trackedDownloadStates.get(uri).getKeys();
}
public int toggleDownload(Activity activity, String name, Uri uri, String extension) {
if (isDownloaded(uri)) {
Log.e(TAG,"isDownloaded");
DownloadAction removeAction =
getDownloadHelper(uri, extension).getRemoveAction(Util.getUtf8Bytes(name));
startServiceWithAction(removeAction);
return -1;
} else {
StartDownloadDialogHelper helper =
new StartDownloadDialogHelper(activity, getDownloadHelper(uri, extension), name);
helper.prepare();
return helper.getTaskId();
}
}
#Override
public void onInitialized(DownloadManager downloadManager) {
// Do nothing.
}
#Override
public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) {
DownloadAction action = taskState.action;
Uri uri = action.uri;
if ((action.isRemoveAction && taskState.state == TaskState.STATE_COMPLETED)
|| (!action.isRemoveAction && taskState.state == TaskState.STATE_FAILED)) {
// A download has been removed, or has failed. Stop tracking it.
if (trackedDownloadStates.remove(uri) != null) {
handleTrackedDownloadStatesChanged();
}
}
}
#Override
public void onIdle(DownloadManager downloadManager) {
// Do nothing.
}
// Internal methods
private void loadTrackedActions(DownloadAction.Deserializer[] deserializers) {
try {
DownloadAction[] allActions = actionFile.load(deserializers);
for (DownloadAction action : allActions) {
trackedDownloadStates.put(action.uri, action);
}
} catch (IOException e) {
Log.e(TAG, "Failed to load tracked actions", e);
}
}
private void handleTrackedDownloadStatesChanged() {
for (Listener listener : listeners) {
listener.onDownloadsChanged();
}
final DownloadAction[] actions = trackedDownloadStates.values().toArray(new DownloadAction[0]);
Log.e(TAG,"actions: "+actions.toString());
actionFileWriteHandler.post(
() -> {
try {
actionFile.store(actions);
} catch (IOException e) {
Log.e(TAG, "Failed to store tracked actions", e);
}
});
}
private void startDownload(DownloadAction action) {
if (trackedDownloadStates.containsKey(action.uri)) {
// This content is already being downloaded. Do nothing.
Log.e(TAG,"download already exsit");
return;
}
trackedDownloadStates.put(action.uri, action);
handleTrackedDownloadStatesChanged();
startServiceWithAction(action);
}
private void startServiceWithAction(DownloadAction action) {
DownloadService.startWithAction(context, MediaDownloadService.class, action, false);
}
private DownloadHelper getDownloadHelper(Uri uri, String extension) {
int type = Util.inferContentType(uri, extension);
switch (type) {
case C.TYPE_DASH:
return new DashDownloadHelper(uri, dataSourceFactory);
case C.TYPE_SS:
return new SsDownloadHelper(uri, dataSourceFactory);
case C.TYPE_HLS:
return new HlsDownloadHelper(uri, dataSourceFactory);
case C.TYPE_OTHER:
return new ProgressiveDownloadHelper(uri);
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
private final class StartDownloadDialogHelper
implements DownloadHelper.Callback, DialogInterface.OnClickListener {
private final DownloadHelper downloadHelper;
private final String name;
private final AlertDialog.Builder builder;
private final View dialogView;
private final List<TrackKey> trackKeys;
private final ArrayAdapter<String> trackTitles;
private final ListView representationList;
private int taskId;
public StartDownloadDialogHelper(
Activity activity, DownloadHelper downloadHelper, String name) {
this.downloadHelper = downloadHelper;
this.name = name;
builder =
new AlertDialog.Builder(activity)
.setTitle(R.string.exo_download_description)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null);
// Inflate with the builder's context to ensure the correct style is used.
LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
dialogView = dialogInflater.inflate(R.layout.start_download_dialog, null);
trackKeys = new ArrayList<>();
trackTitles =
new ArrayAdapter<>(
builder.getContext(), android.R.layout.simple_list_item_multiple_choice);
representationList = dialogView.findViewById(R.id.representation_list);
representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
representationList.setAdapter(trackTitles);
}
public void prepare() {
downloadHelper.prepare(this);
}
#Override
public void onPrepared(DownloadHelper helper) {
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
TrackGroupArray trackGroups = downloadHelper.getTrackGroups(i);
for (int j = 0; j < trackGroups.length; j++) {
TrackGroup trackGroup = trackGroups.get(j);
for (int k = 0; k < trackGroup.length; k++) {
trackKeys.add(new TrackKey(i, j, k));
trackTitles.add(trackNameProvider.getTrackName(trackGroup.getFormat(k)));
}
}
}
if (!trackKeys.isEmpty()) {
builder.setView(dialogView);
}
builder.create().show();
}
#Override
public void onPrepareError(DownloadHelper helper, IOException e) {
Toast.makeText(
context.getApplicationContext(), R.string.download_start_error, Toast.LENGTH_LONG)
.show();
Log.e(TAG, "Failed to start download", e);
}
#Override
public void onClick(DialogInterface dialog, int which) {
ArrayList<TrackKey> selectedTrackKeys = new ArrayList<>();
for (int i = 0; i < representationList.getChildCount(); i++) {
if (representationList.isItemChecked(i)) {
selectedTrackKeys.add(trackKeys.get(i));
}
}
if (!selectedTrackKeys.isEmpty() || trackKeys.isEmpty()) {
// We have selected keys, or we're dealing with single stream content.
DownloadAction downloadAction =
downloadHelper.getDownloadAction(Util.getUtf8Bytes(name), selectedTrackKeys);
taskId=MyApplication.getInstance().getDownloadManager().handleAction(downloadAction);
startDownload(downloadAction);
}
}
}
}
In my Fragment/Activity:
/* this method will be called when user click on download button of each item */
#Override
public void onDownloadClick(LectureList lecture) {
Log.e(TAG,"onClickDownload");
downloadTracker.toggleDownload(this,lecture.getTitle_lecture(),
Uri.parse(lecture.getUrlPath()),lecture.getExtension());
}
And here is my broadcast receiver:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG,"onRecive download");
if(intent.getAction().equals(MESSAGE_PROGRESS)){
int progress=intent.getLongExtra("progress",0);
}
}
};
In the getForegroundNotification() method, you will get list of TaskState objects, which has a members downloadPercentage and your download Uri taskState.action.uri which is unique for each download task. Store these variables into a map and broadcast the map.
override fun getForegroundNotification(taskStates: Array<TaskState>): Notification {
var totalPercentage = 0f
var downloadTaskCount = 0
var progressMap : HashMap<Uri, Int> = HashMap()
for (taskState in taskStates) {
if (taskState.state != TaskState.STATE_STARTED && taskState.state != TaskState.STATE_COMPLETED) {
continue
}
if (taskState.action.isRemoveAction) {
continue
}
if (taskState.downloadPercentage != C.PERCENTAGE_UNSET.toFloat()) {
totalPercentage += taskState.downloadPercentage
progressMap.put(taskState.action.uri, taskState.downloadPercentage.toInt())
}
downloadTaskCount++
}
var progress = 0
progress = (totalPercentage / downloadTaskCount).toInt()
broadcastIndividualProgress(progressMap)
return buildProgressNotification(progress)
}
Im taking presets fields from the database and display them in ListView using PresetsListAdapter.
Coded as below:
public class BasicAcActivity extends Activity implements OnCheckedChangeListener {
private static final String TAG = BasicAcActivity.class.getName();
public static volatile PresetsListAdapter presetsListAdapter;
public static volatile SwitchCompat basicEQToggle;
private ListView mListView;
private FlipBeatsDataManager mDataManager;
private PresetsManager mPrstManager;
private SharedPreferences mShrdPrefs;
private SharedPreferences mShrdPrefPresets;
private TextView tvPresets;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.basic_eq_settings);
initialize();
String text = Utilities.getResourceValue(this, R.string.equalizer_presets);
tvPresets.setText(text);
// Get presets
List<Preset> presets = generatePresets();
if (presets != null && presets.size() > 0)
{
for (Preset preset : presets)
{
mDataManager.createPreset(preset);
}
}
List<Preset> dbPresets = mDataManager.getMandotoryPresets();
presetsListAdapter = new PresetsListAdapter(this, dbPresets);
basicEQToggle.setChecked(true);
mListView.setAdapter(presetsListAdapter);
String onOrOff = mShrdPrefPresets.getString("Presets", "on");
if (onOrOff.equalsIgnoreCase("on"))
{
basicEQToggle.setChecked(true);
}
else
{
basicEQToggle.setChecked(false);
}
basicEQToggle.setOnCheckedChangeListener(this);
}
private void initialize()
{
tvPresets = (TextView) findViewById(R.id.lbl_presets);
mListView = (ListView) findViewById(R.id.presets_list);
basicEQToggle = (SwitchCompat) findViewById(R.id.basic_eq_toggle);
tvPresets.setTypeface(CommonUtils.getTfRobotoRegFont());
mPrstManager = PresetsManager.getInstance(getApplicationContext());
mDataManager = FlipBeatsDataManager.getInstance(getApplicationContext());
mShrdPrefs = getApplicationContext().getSharedPreferences("Prefs", Context.MODE_PRIVATE);
mShrdPrefPresets = getApplicationContext().getSharedPreferences("Preset", Context.MODE_PRIVATE);
}
private List<Preset> generatePresets()
{
// Default value is set if 'equalizer' is null
short bands = 5;
if (PlayerService.sEqualizer != null)
{
try
{
bands = PlayerService.sEqualizer.getNumberOfBands();
}
catch (Exception | Error e)
{
Log.w(TAG, "-- createPresets " + e.getMessage());
}
}
// Create Preset Based on the Bands Level
return mPrstManager.createPresets(bands);
}
#Override
protected void onResume()
{
super.onResume();
setLastUsedType();
boolean isSelected = mDataManager.findMandatoryPresetSelected();
if (!isSelected)
{
int preselect = mDataManager.getLastSelectedPresetId();
mDataManager.updateSelectedToPro1(preselect);
}
presetsListAdapter.notifyDataSetChanged();
Activity actv = getParent();
if (actv != null && actv instanceof AudioConfigurationActivity)
{
AudioConfigurationActivity parent = (AudioConfigurationActivity) actv;
// Sets default advance settings.
parent.setDefaultAdvanceSettings();
parent.setThroughBasicActivity();
}
setDefaultAdvancedSettings();
}
#Override
public boolean onKeyUp(int keyCode, #NonNull KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_VOLUME_UP)
{
if (SoundHealth.getSoundHealth(this))
{
Message.alertMsg(this, Utilities.getResourceValue(this, R.string.sound_health),
Utilities.getResourceValue(this, R.string.sound_health_msg));
}
return true;
}
return super.onKeyUp(keyCode, event);
}
#Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked)
{
if (isChecked)
{
basicEQToggle.setSelected(true);
if (PlayerService.sEqualizer != null)
{
try
{
PlayerService.sEqualizer.setEnabled(true);
}
catch (Exception | Error e)
{
Log.w(TAG, "-- onCheckedChanged " + e.getMessage());
}
}
getApplicationContext();
mShrdPrefPresets.edit().putString("Presets", "on").apply();
if (BasicAcActivity.presetsListAdapter != null)
{
BasicAcActivity.presetsListAdapter.mPresets = mDataManager.getMandotoryPresets();
}
presetsListAdapter.notifyDataSetChanged();
}
else
{
basicEQToggle.setSelected(false);
if (PlayerService.sEqualizer != null)
{
try
{
PlayerService.sEqualizer.setEnabled(false);
}
catch (Exception | Error e)
{
Log.w(TAG, "-- onCheckedChanged " + e.getMessage());
}
}
if (BasicAcActivity.presetsListAdapter != null)
{
BasicAcActivity.presetsListAdapter.setPreset(14);
}
mShrdPrefPresets.edit().putString("Presets", "off").commit();
if (BasicAcActivity.presetsListAdapter != null)
{
BasicAcActivity.presetsListAdapter.mPresets = mDataManager.getMandotoryPresets();
}
presetsListAdapter.notifyDataSetChanged();
}
}
private void setLastUsedType()
{
mShrdPrefs.edit().putString(AudioConfigurationActivity.AU_SET_TYPE_KEY,
AudioConfigurationActivity.AU_SET_TYPE_BASIC);
}
/*
* Sets default advanced sound settings
*/
private void setDefaultAdvancedSettings()
{
try
{
if (PlayerService.sAudioDefaultValues != null)
{
AudioDefaultValues def = PlayerService.sAudioDefaultValues;
PlayerService.sBaseBooster.setStrength(def.getBassBoost());
PlayerService.sSurroundSound.setStrength(def.getSurroundSound());
PlayerService.sPresetReverb.setPreset(def.getRoomSize());
/** EnvironmentalReverb attributes */
PlayerService.sEnvironmentalReverb.setDecayHFRatio(def.getDecayHfRatio());
PlayerService.sEnvironmentalReverb.setDecayTime(def.getDecayTime());
PlayerService.sEnvironmentalReverb.setDensity(def.getDensity());
PlayerService.sEnvironmentalReverb.setDiffusion(def.getDiffusion());
PlayerService.sEnvironmentalReverb.setReflectionsDelay(def.getReflectionsDelay());
PlayerService.sEnvironmentalReverb.setReflectionsLevel(def.getReflectionsLevel());
PlayerService.sEnvironmentalReverb.setReverbDelay(def.getReverbDelay());
PlayerService.sEnvironmentalReverb.setReverbLevel(def.getReverbLevel());
PlayerService.sEnvironmentalReverb.setRoomHFLevel(def.getRoomHfLevel());
PlayerService.sEnvironmentalReverb.setRoomLevel(def.getRoomLevel());
}
}
catch (Exception e)
{
Log.w(TAG, "--- setDefaultAdvancedSettings, " + e.getMessage());
}
}
}
Adapter class :
abstract class BeatsBaseAdapter extends BaseAdapter {
final ThemeManager mThemeManager;
Context mContext;
Activity mActivity;
ImageLoader mImageLoader;
int mCategory;
FlipBeatsDataManager mDataManager;
int txtColorNumber;
int colorPrimaryText;
int colorSecondaryText;
int colorListBg;
int seperatorBg;
int listDrawbleBg;
int flipCardBg;
int dashboardCardBg;
/**
* Base Constructor
*
* #param context Application Context
*/
FlipBeatsBaseAdapter(Context context) {
mContext = context;
mDataManager = FlipBeatsDataManager.getInstance(mContext);
int sysThemeId = ThemeUtils.getTheme(mContext);
mThemeManager = ThemeManager.getInstance(mContext, sysThemeId);
}
/**
* Base Constructor
*
* #param activity Current Activity
*/
FlipBeatsBaseAdapter(Activity activity) {
if (activity != null) {
mContext = activity.getApplicationContext();
mActivity = activity;
mDataManager = FlipBeatsDataManager.getInstance(mContext);
int sysThemeId = ThemeUtils.getTheme(mContext);
mThemeManager = ThemeManager.getInstance(mContext, sysThemeId);
if (FlipBeatsGlobals.isBlackEditionActive) {
txtColorNumber = mContext.getResources().getColor(R.color.color_app_txt_numbers_dark);
colorPrimaryText = mContext.getResources().getColor(R.color.color_app_text_color_dark);
colorSecondaryText = mContext.getResources().getColor(R.color.color_app_secondary_text_color_dark);
colorListBg = mContext.getResources().getColor(R.color.color_black);
seperatorBg = mContext.getResources().getColor(R.color.color_app_disable_color_dark);
listDrawbleBg = R.drawable.list_front_bg_dark;
flipCardBg = mContext.getResources().getColor(R.color.color_frame_menu_dark);
dashboardCardBg = mContext.getResources().getColor(R.color.color_app_txt_disable_dark);
} else {
txtColorNumber = mContext.getResources().getColor(R.color.color_app_txt_numbers);
colorPrimaryText = mContext.getResources().getColor(R.color.color_app_text_color);
colorSecondaryText = mContext.getResources().getColor(R.color.color_app_secondary_text_color);
colorListBg = mContext.getResources().getColor(R.color.color_white);
seperatorBg = mContext.getResources().getColor(R.color.color_app_disable_color);
listDrawbleBg = R.drawable.list_front_bg;
flipCardBg = mContext.getResources().getColor(R.color.color_frame_menu);
dashboardCardBg = mContext.getResources().getColor(R.color.color_app_txt_disable);
}
} else {
mThemeManager = null;
}
}
}
This works fine until API level 25 but for Android Oreo version the ListView is not displayed, each item is only displayed when it is selected. What is possibly gone wrong here?
Thanks in advance.
Edit: actually the colorListBg is not setting for those devices. that's why the list is not displayed as the text color is set to white color
I have custom ImageView for animated GIF image. i want to show GIF image, I tried but in this case it is contain url in Async instead I want to show GIF image from raw folder without using Glide. Anyone have any idea how to show image? Please guyz help to solve this problem!!!
I tried this for set raw file
new GifStaticData() {
#Override
protected void onPostExecute(Resource drawable) {
super.onPostExecute(drawable);
gifImageView.setImageResource(R.raw.earth_tilt_animation);
// Log.d(TAG, "GIF width is " + gifImageView.getGifWidth());
// Log.d(TAG, "GIF height is " + gifImageView.getGifHeight());
}
}.execute(R.raw.earth_tilt_animation);
GifStaticData.java
public class GifStaticData extends AsyncTask<Resource, Void, Resource> {
private static final String TAG = "GifDataDownloader";
#Override protected Resource doInBackground(final Resource... params) {
final Resource gifUrl = params[0];
if (gifUrl == null)
return null;
try {
// return ByteArrayHttpClient.get(gifUrl);
return gifUrl;
} catch (OutOfMemoryError e) {
Log.e(TAG, "GifDecode OOM: " + gifUrl, e);
return null;
}
}
}
GifImageView.java
public class GifImageView extends ImageView implements Runnable {
private static final String TAG = "GifDecoderView";
private GifDecoder gifDecoder;
private Bitmap tmpBitmap;
private final Handler handler = new Handler(Looper.getMainLooper());
private boolean animating;
private boolean shouldClear;
private Thread animationThread;
private OnFrameAvailable frameCallback = null;
private long framesDisplayDuration = -1L;
private OnAnimationStop animationStopCallback = null;
private final Runnable updateResults = new Runnable() {
#Override
public void run() {
if (tmpBitmap != null && !tmpBitmap.isRecycled()) {
setImageBitmap(tmpBitmap);
}
}
};
private final Runnable cleanupRunnable = new Runnable() {
#Override
public void run() {
tmpBitmap = null;
gifDecoder = null;
animationThread = null;
shouldClear = false;
}
};
public GifImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public GifImageView(final Context context) {
super(context);
}
public void setBytes(final byte[] bytes) {
gifDecoder = new GifDecoder();
try {
gifDecoder.read(bytes);
gifDecoder.advance();
} catch (final OutOfMemoryError e) {
gifDecoder = null;
Log.e(TAG, e.getMessage(), e);
return;
}
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public long getFramesDisplayDuration() {
return framesDisplayDuration;
}
/**
* Sets custom display duration in milliseconds for the all frames. Should be called before {#link
* #startAnimation()}
*
* #param framesDisplayDuration Duration in milliseconds. Default value = -1, this property will
* be ignored and default delay from gif file will be used.
*/
public void setFramesDisplayDuration(long framesDisplayDuration) {
this.framesDisplayDuration = framesDisplayDuration;
}
public void startAnimation() {
animating = true;
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public boolean isAnimating() {
return animating;
}
public void stopAnimation() {
animating = false;
if (animationThread != null) {
animationThread.interrupt();
animationThread = null;
}
}
public void clear() {
animating = false;
shouldClear = true;
stopAnimation();
handler.post(cleanupRunnable);
}
private boolean canStart() {
return animating && gifDecoder != null && animationThread == null;
}
public int getGifWidth() {
return gifDecoder.getWidth();
}
public int getGifHeight() {
return gifDecoder.getHeight();
}
#Override public void run() {
if (shouldClear) {
handler.post(cleanupRunnable);
return;
}
final int n = gifDecoder.getFrameCount();
do {
for (int i = 0; i < n; i++) {
if (!animating) {
break;
}
//milliseconds spent on frame decode
long frameDecodeTime = 0;
try {
long before = System.nanoTime();
tmpBitmap = gifDecoder.getNextFrame();
frameDecodeTime = (System.nanoTime() - before) / 1000000;
if (frameCallback != null) {
tmpBitmap = frameCallback.onFrameAvailable(tmpBitmap);
}
if (!animating) {
break;
}
handler.post(updateResults);
} catch (final ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
Log.w(TAG, e);
}
if (!animating) {
break;
}
gifDecoder.advance();
try {
int delay = gifDecoder.getNextDelay();
// Sleep for frame duration minus time already spent on frame decode
// Actually we need next frame decode duration here,
// but I use previous frame time to make code more readable
delay -= frameDecodeTime;
if (delay > 0) {
Thread.sleep(framesDisplayDuration > 0 ? framesDisplayDuration : delay);
}
} catch (final Exception e) {
// suppress any exception
// it can be InterruptedException or IllegalArgumentException
}
}
} while (animating);
if (animationStopCallback != null) {
animationStopCallback.onAnimationStop();
}
}
public OnFrameAvailable getOnFrameAvailable() {
return frameCallback;
}
public void setOnFrameAvailable(OnFrameAvailable frameProcessor) {
this.frameCallback = frameProcessor;
}
public interface OnFrameAvailable {
Bitmap onFrameAvailable(Bitmap bitmap);
}
public OnAnimationStop getOnAnimationStop() {
return animationStopCallback;
}
public void setOnAnimationStop(OnAnimationStop animationStop) {
this.animationStopCallback = animationStop;
}
public interface OnAnimationStop {
void onAnimationStop();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
clear();
}
}
I had to play and pause the Gif image Glide - Cannot stop gif onClick- Getting TransitionDrawable instead of Animate/GifDrawable
The idea is to get drawable from view,checking if it is an instance of Gifdrawable and playing and pausing it.(Hoping the gif image is already playing)
Add this In OnClick of GifImageView
Drawable drawable = ((ImageView) v).getDrawable();
if (drawable instanceof GifDrawable) {
GifDrawable animatable = (GifDrawable) drawable;
if (animatable.isRunning()) {
animatable.stop();
} else {
animatable.start();
}
}
I found the solution of above problem using GifMovieView!!!
GifMovieViewer.java
public class GifMovieViewer extends Activity {
private Button btnStart;
private GifMovieView gif1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gif_movie_viewer);
gif1 = (GifMovieView) findViewById(R.id.gif1);
btnStart = (Button) findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gif1.setMovieResource(R.drawable.earth_tilt_animation);
//for pause
// gif1.setPaused(gif1.isPaused());
}
});
}
public void onGifClick(View v) {
GifMovieView gif = (GifMovieView) v;
gif.setPaused(!gif.isPaused());
}
}
Im using Exoplayer for HLS Streaming in my App. Its playing nicely but when i disconnect the internet connection and enable it again,Exo player does not resume the video play.
Exoplayer is handling this by default or do i need to manually handle this?
here is my code..`
public class PlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener,
DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener,
AudioCapabilitiesReceiver.Listener { public class PlayerActivity extends Activity implements SurfaceHolder.Callback, OnClickListener,
DemoPlayer.Listener, DemoPlayer.CaptionListener, DemoPlayer.Id3MetadataListener,
AudioCapabilitiesReceiver.Listener {
// For use within demo app code.
public static final String CONTENT_ID_EXTRA = "content_id";
public static final String CONTENT_TYPE_EXTRA = "content_type";
public static final String PROVIDER_EXTRA = "provider";
// For use when launching the demo app using adb.
private static final String CONTENT_EXT_EXTRA = "type";
private static final String TAG = "PlayerActivity";
private static final int MENU_GROUP_TRACKS = 1;
private static final int ID_OFFSET = 2;
private static final CookieManager defaultCookieManager;
static {
defaultCookieManager = new CookieManager();
defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
}
private EventLogger eventLogger;
private MediaController mediaController;
private View debugRootView;
private View shutterView;
private AspectRatioFrameLayout videoFrame;
private SurfaceView surfaceView;
private TextView debugTextView;
private TextView playerStateTextView;
private SubtitleLayout subtitleLayout;
private Button videoButton;
private Button audioButton;
private Button textButton;
private Button retryButton;
static TextView bitrateTextView;
private static DemoPlayer player;
private DebugTextViewHelper debugViewHelper;
private boolean playerNeedsPrepare;
private long playerPosition;
private boolean enableBackgroundAudio;
private Uri contentUri;
private int contentType;
private String contentId;
private String provider;
RotateAnimation rotate;
ImageView rotateLoad=null;
ImageView loadMid=null;
FrameLayout videoLoad;
private String vidLink ="";
private String title =""; private TextView vodTitle;
private String description =""; private TextView vodDesc;
private String vodimage =""; private ImageView vodThumb;
private String chimage =""; private ImageView chLogo;
private String datetitle =""; private TextView vodTimeDesc, videoCurrentTime, videoTimeEnd;
private Bitmap vodImgThumb, chImgLogo;
private static FrameLayout guideInfo;
private FrameLayout seekBar;
private FrameLayout playPause;
private int rewindRate = 1;
private int forwardRate = 1, stopPosition ;
private SeekBar sb;
CountDownTimer ct;
int infoFade = 0 , seekFade =0 , height, width;
private boolean isPlaying = false;
static long storeBitRate;
private AudioCapabilitiesReceiver audioCapabilitiesReceiver;
// Activity lifecycle
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.player_activity);
View root = findViewById(R.id.root);
shutterView = findViewById(R.id.shutter);
debugRootView = findViewById(R.id.controls_root);
videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame);
surfaceView = (SurfaceView) findViewById(R.id.surface_view);
surfaceView.getHolder().addCallback(this);
debugTextView = (TextView) findViewById(R.id.debug_text_view);
playerStateTextView = (TextView) findViewById(R.id.player_state_view);
subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles);
mediaController = new KeyCompatibleMediaController(this);
mediaController.setAnchorView(root);
// retryButton = (Button) findViewById(R.id.retry_button);
// retryButton.setOnClickListener(this);
videoButton = (Button) findViewById(R.id.video_controls);
audioButton = (Button) findViewById(R.id.audio_controls);
textButton = (Button) findViewById(R.id.text_controls);
playPause = (FrameLayout)findViewById(R.id.videoPlayPause);
videoLoad = (FrameLayout) findViewById(R.id.videoLoad);
sb = (SeekBar)findViewById(R.id.seekBar1);
// Guide Info Animator
guideInfo = (FrameLayout)findViewById(R.id.guide_info);
seekBar = (FrameLayout)findViewById(R.id.video_seek);
playPause = (FrameLayout)findViewById(R.id.videoPlayPause);
videoCurrentTime = (TextView)findViewById(R.id.video_timestart);
bitrateTextView=(TextView)findViewById(R.id.bitratetext);
videoTimeEnd = (TextView)findViewById(R.id.video_timeend);
seekBar.setVisibility(View.GONE);
playPause.setVisibility(View.GONE);
root.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE
|| keyCode == KeyEvent.KEYCODE_MENU) {
return false;
}
if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
}
return mediaController.dispatchKeyEvent(event);
}
});
CookieHandler currentHandler = CookieHandler.getDefault();
if (currentHandler != defaultCookieManager) {
CookieHandler.setDefault(defaultCookieManager);
}
audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, this);
audioCapabilitiesReceiver.register();
}
#Override
public void onNewIntent(Intent intent) {
releasePlayer();
playerPosition = 0;
setIntent(intent);
}
#Override
public void onResume() {
super.onResume();
Intent intent = getIntent();
Bundle extras = getIntent().getExtras();
contentUri = intent.getData();
contentType = Util.TYPE_HLS;
title = extras.getString("title", title);
description = extras.getString("description", description);
vodimage = extras.getString("vodimage", vodimage);
chimage = extras.getString("chimage", chimage);
datetitle = extras.getString("datetitle", datetitle);
// Set Data
vodTitle = (TextView)findViewById(R.id.vodTitle);
vodTitle.setText(title);
vodDesc = (TextView)findViewById(R.id.vodDesc);
/* DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(player.getMainHandler(), null);
String dfg=bandwidthMeter.getBitrateEstimate()+"";*/
vodDesc.setText(description);
vodThumb = (ImageView)findViewById(R.id.vodThumb);
chLogo = (ImageView)findViewById(R.id.chLogo);
vodTimeDesc = (TextView)findViewById(R.id.vodTimeDesc);
vodTimeDesc.setText(datetitle);
rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(2000);
rotate.setRepeatCount(Animation.INFINITE);
rotate.setInterpolator(new LinearInterpolator());
rotateLoad= (ImageView) findViewById(R.id.lycaLoadMid_rotate);
loadMid = (ImageView) findViewById(R.id.lycaLoadMid);
rotateLoad.startAnimation(rotate);
videoLoad = (FrameLayout) findViewById(R.id.videoLoad);
//Gathering images
LoadImages loadImage= new LoadImages ();
loadImage.execute(vodimage,chimage);
if (player == null) {
// if (!maybeRequestPermission()) {
preparePlayer(true);
//}
} else {
player.setBackgrounded(false);
}
}
#Override
public void onPause() {
super.onPause();
if (!enableBackgroundAudio) {
releasePlayer();
} else {
player.setBackgrounded(true);
}
shutterView.setVisibility(View.VISIBLE);
}
#Override
public void onDestroy() {
super.onDestroy();
audioCapabilitiesReceiver.unregister();
releasePlayer();
}
// OnClickListener methods
#Override
public void onClick(View view) {
if (view == retryButton) {
preparePlayer(true);
}
}
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null;
}
// AudioCapabilitiesReceiver.Listener methods
#Override
public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) {
if (player == null) {
return;
}
boolean backgrounded = player.getBackgrounded();
boolean playWhenReady = player.getPlayWhenReady();
releasePlayer();
preparePlayer(playWhenReady);
player.setBackgrounded(backgrounded);
}
// Permission request listener method
// Internal methods
private RendererBuilder getRendererBuilder() {
String userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
switch (contentType) {
case Util.TYPE_SS:
return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(),
new SmoothStreamingTestMediaDrmCallback());
case Util.TYPE_DASH:
return new DashRendererBuilder(this, userAgent, contentUri.toString(),
new WidevineTestMediaDrmCallback(contentId, provider));
case Util.TYPE_HLS:
return new HlsRendererBuilder(this, userAgent, contentUri.toString());
case Util.TYPE_OTHER:
return new ExtractorRendererBuilder(this, userAgent, contentUri);
default:
throw new IllegalStateException("Unsupported type: " + contentType);
}
}
private void preparePlayer(boolean playWhenReady) {
if (player == null) {
player = new DemoPlayer(getRendererBuilder());
player.addListener(this);
player.setCaptionListener(this);
player.setMetadataListener(this);
player.seekTo(playerPosition);
playerNeedsPrepare = true;
mediaController.setMediaPlayer(player.getPlayerControl());
mediaController.setEnabled(true);
eventLogger = new EventLogger();
eventLogger.startSession();
player.addListener(eventLogger);
player.setInfoListener(eventLogger);
player.setInternalErrorListener(eventLogger);
//debugViewHelper = new DebugTextViewHelper(player, debugTextView);
// debugViewHelper.start();
}
if (playerNeedsPrepare) {
player.prepare();
playerNeedsPrepare = false;
updateButtonVisibilities();
}
player.setSurface(surfaceView.getHolder().getSurface());
player.setPlayWhenReady(playWhenReady);
guideInfo.setVisibility(View.VISIBLE);
guideInfo.postDelayed(new Runnable() { public void run() { guideInfo.setVisibility(View.GONE); } }, 5000);
}
private void releasePlayer() {
if (player != null) {
debugViewHelper.stop();
debugViewHelper = null;
playerPosition = player.getCurrentPosition();
player.release();
player = null;
eventLogger.endSession();
eventLogger = null;
}
}
// DemoPlayer.Listener implementation
#Override
public void onStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED) {
showControls();
}
if (playbackState == ExoPlayer.STATE_BUFFERING) {
if(videoLoad.getVisibility()==View.GONE){
videoLoad.setVisibility(View.VISIBLE);
}
}
if (playbackState == ExoPlayer.STATE_READY) {
videoLoad.setVisibility(View.GONE);
}
if (playbackState == ExoPlayer.STATE_ENDED) {
videoLoad.setVisibility(View.GONE);
finish();
}
if(playWhenReady){
}
String text = "playWhenReady=" + playWhenReady + ", playbackState=";
switch(playbackState) {
case ExoPlayer.STATE_BUFFERING:
text += "buffering";
break;
case ExoPlayer.STATE_ENDED:
text += "ended";
break;
case ExoPlayer.STATE_IDLE:
text += "idle";
break;
case ExoPlayer.STATE_PREPARING:
text += "preparing";
break;
case ExoPlayer.STATE_READY:
text += "ready";
break;
default:
text += "unknown";
break;
}
// playerStateTextView.setText(text);
updateButtonVisibilities();
}
#Override
public void onError(Exception e) {
String errorString = null;
if (e instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e;
errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
} else if (e instanceof ExoPlaybackException
&& e.getCause() instanceof DecoderInitializationException) {
// Special case for decoder initialization failures.
DecoderInitializationException decoderInitializationException =
(DecoderInitializationException) e.getCause();
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof DecoderQueryException) {
errorString = getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
}
else {
errorString = getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
}
}
if (errorString != null) {
Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_LONG).show();
}
playerNeedsPrepare = true;
updateButtonVisibilities();
showControls();
}
#Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthAspectRatio) {
shutterView.setVisibility(View.GONE);
videoFrame.setAspectRatio(
height == 0 ? 1 : (width * pixelWidthAspectRatio) / height);
}
// User controls
private void updateButtonVisibilities() {
// retryButton.setVisibility(playerNeedsPrepare ? View.VISIBLE : View.GONE);
videoButton.setVisibility(haveTracks(DemoPlayer.TYPE_VIDEO) ? View.VISIBLE : View.GONE);
audioButton.setVisibility(haveTracks(DemoPlayer.TYPE_AUDIO) ? View.VISIBLE : View.GONE);
textButton.setVisibility(haveTracks(DemoPlayer.TYPE_TEXT) ? View.VISIBLE : View.GONE);
}
private boolean haveTracks(int type) {
return player != null && player.getTrackCount(type) > 0;
}
public void showVideoPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
configurePopupWithTracks(popup, null, DemoPlayer.TYPE_VIDEO);
popup.show();
}
public void showAudioPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
Menu menu = popup.getMenu();
menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.enable_background_audio);
final MenuItem backgroundAudioItem = menu.findItem(0);
backgroundAudioItem.setCheckable(true);
backgroundAudioItem.setChecked(enableBackgroundAudio);
OnMenuItemClickListener clickListener = new OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
if (item == backgroundAudioItem) {
enableBackgroundAudio = !item.isChecked();
return true;
}
return false;
}
};
configurePopupWithTracks(popup, clickListener, DemoPlayer.TYPE_AUDIO);
popup.show();
}
public void showTextPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
configurePopupWithTracks(popup, null, DemoPlayer.TYPE_TEXT);
popup.show();
}
public void showVerboseLogPopup(View v) {
PopupMenu popup = new PopupMenu(this, v);
Menu menu = popup.getMenu();
menu.add(Menu.NONE, 0, Menu.NONE, R.string.logging_normal);
menu.add(Menu.NONE, 1, Menu.NONE, R.string.logging_verbose);
menu.setGroupCheckable(Menu.NONE, true, true);
menu.findItem((VerboseLogUtil.areAllTagsEnabled()) ? 1 : 0).setChecked(true);
popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == 0) {
VerboseLogUtil.setEnableAllTags(false);
} else {
VerboseLogUtil.setEnableAllTags(true);
}
return true;
}
});
popup.show();
}
private void configurePopupWithTracks(PopupMenu popup,
final OnMenuItemClickListener customActionClickListener,
final int trackType) {
if (player == null) {
return;
}
int trackCount = player.getTrackCount(trackType);
if (trackCount == 0) {
return;
}
popup.setOnMenuItemClickListener(new OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
return (customActionClickListener != null
&& customActionClickListener.onMenuItemClick(item))
|| onTrackItemClick(item, trackType);
}
});
Menu menu = popup.getMenu();
// ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0).
menu.add(MENU_GROUP_TRACKS, DemoPlayer.TRACK_DISABLED + ID_OFFSET, Menu.NONE, R.string.off);
for (int i = 0; i < trackCount; i++) {
menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE,
buildTrackName(player.getTrackFormat(trackType, i)));
}
menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true);
menu.findItem(player.getSelectedTrack(trackType) + ID_OFFSET).setChecked(true);
}
private static String buildTrackName(MediaFormat format) {
if (format.adaptive) {
return "auto";
}
String trackName;
if (MimeTypes.isVideo(format.mimeType)) {
trackName = joinWithSeparator(joinWithSeparator(buildResolutionString(format),
buildBitrateString(format)), buildTrackIdString(format));
} else if (MimeTypes.isAudio(format.mimeType)) {
trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format),
buildAudioPropertyString(format)), buildBitrateString(format)),
buildTrackIdString(format));
} else {
trackName = joinWithSeparator(joinWithSeparator(buildLanguageString(format),
buildBitrateString(format)), buildTrackIdString(format));
}
return trackName.length() == 0 ? "unknown" : trackName;
}
private static String buildResolutionString(MediaFormat format) {
return format.width == MediaFormat.NO_VALUE || format.height == MediaFormat.NO_VALUE
? "" : format.width + "x" + format.height;
}
private static String buildAudioPropertyString(MediaFormat format) {
return format.channelCount == MediaFormat.NO_VALUE || format.sampleRate == MediaFormat.NO_VALUE
? "" : format.channelCount + "ch, " + format.sampleRate + "Hz";
}
private static String buildLanguageString(MediaFormat format) {
return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? ""
: format.language;
}
private static String buildBitrateString(MediaFormat format) {
String s=format.bitrate == MediaFormat.NO_VALUE ? ""
: String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f);
// Toast.makeText(con, s, Toast.LENGTH_LONG).show();
return s;
}
private static String joinWithSeparator(String first, String second) {
return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second);
}
private static String buildTrackIdString(MediaFormat format) {
return format.trackId == null ? "" : " (" + format.trackId + ")";
}
private boolean onTrackItemClick(MenuItem item, int type) {
if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) {
return false;
}
player.setSelectedTrack(type, item.getItemId() - ID_OFFSET);
return true;
}
private void toggleControlsVisibility() { /*/////////////////////////////////// Showing defalut controllers */
if (mediaController.isShowing()) {
mediaController.hide();
debugRootView.setVisibility(View.GONE);
} else {
showControls();
}
}
private void showControls() {
mediaController.show(0);
debugRootView.setVisibility(View.VISIBLE);
}
// DemoPlayer.CaptionListener implementation
#Override
public void onCues(List<Cue> cues) {
subtitleLayout.setCues(cues);
}
// DemoPlayer.MetadataListener implementation
#Override
public void onId3Metadata(Map<String, Object> metadata) {
for (Map.Entry<String, Object> entry : metadata.entrySet()) {
if (TxxxMetadata.TYPE.equals(entry.getKey())) {
TxxxMetadata txxxMetadata = (TxxxMetadata) entry.getValue();
Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s",
TxxxMetadata.TYPE, txxxMetadata.description, txxxMetadata.value));
} else if (PrivMetadata.TYPE.equals(entry.getKey())) {
PrivMetadata privMetadata = (PrivMetadata) entry.getValue();
Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s",
PrivMetadata.TYPE, privMetadata.owner));
} else if (GeobMetadata.TYPE.equals(entry.getKey())) {
GeobMetadata geobMetadata = (GeobMetadata) entry.getValue();
Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s",
GeobMetadata.TYPE, geobMetadata.mimeType, geobMetadata.filename,
geobMetadata.description));
} else {
Log.i(TAG, String.format("ID3 TimedMetadata %s", entry.getKey()));
}
}
}
// SurfaceHolder.Callback implementation
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (player != null) {
player.setSurface(holder.getSurface());
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Do nothing.
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (player != null) {
player.blockingClearSurface();
}
}
private void configureSubtitleView() {
CaptionStyleCompat style;
float fontScale;
if (Util.SDK_INT >= 19) {
style = getUserCaptionStyleV19();
fontScale = getUserCaptionFontScaleV19();
} else {
style = CaptionStyleCompat.DEFAULT;
fontScale = 1.0f;
}
subtitleLayout.setStyle(style);
subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale);
}
#TargetApi(19)
private float getUserCaptionFontScaleV19() {
CaptioningManager captioningManager =
(CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
return captioningManager.getFontScale();
}
#TargetApi(19)
private CaptionStyleCompat getUserCaptionStyleV19() {
CaptioningManager captioningManager =
(CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE);
return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle());
}
/**
* Makes a best guess to infer the type from a media {#link Uri} and an optional overriding file
* extension.
*
* #param uri The {#link Uri} of the media.
* #param fileExtension An overriding file extension.
* #return The inferred type.
*/
private static int inferContentType(Uri uri, String fileExtension) {
String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension
: uri.getLastPathSegment();
return Util.inferContentType(lastPathSegment);
}
private static final class KeyCompatibleMediaController extends MediaController {
private MediaController.MediaPlayerControl playerControl;
public KeyCompatibleMediaController(Context context) {
super(context);
}
#Override
public void setMediaPlayer(MediaController.MediaPlayerControl playerControl) {
super.setMediaPlayer(playerControl);
this.playerControl = playerControl;
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
if (playerControl.canSeekForward() && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
playerControl.seekTo(playerControl.getCurrentPosition() + 15000); // milliseconds
BandwidthMeter bm=player.getBandwidthMeter();
Long l=bm.getBitrateEstimate();
storeBitRate=l;
bitrateTextView.setText(storeBitRate+" bits/sec");
show();
}
return true;
} else if (playerControl.canSeekBackward() && keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
playerControl.seekTo(playerControl.getCurrentPosition() - 15000); // milliseconds
show();
}
return true;
}
return super.dispatchKeyEvent(event);
}
}
private class LoadImages extends AsyncTask<String, String, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//pDialog.setVisibility(View.VISIBLE);
}
protected Void doInBackground(String... args) {
try {
vodImgThumb = BitmapFactory.decodeStream((InputStream)new URL(args[0]).getContent());
chImgLogo = BitmapFactory.decodeStream((InputStream)new URL(args[1]).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
super.onPostExecute(result);
vodThumb.setImageBitmap(vodImgThumb);
chLogo.setImageBitmap(chImgLogo);
}
}
}
`
In demo of ExoPlayer this issue handle with retry button see how implemented here.
First of all set an ErrorListener to determined some error happened then just call following piece of code fix the issue:
if (playerNeedsPrepare) {
player.prepare();
playerNeedsPrepare = false;
updateButtonVisibilities();
}
player.setSurface(surfaceView.getHolder().getSurface());
player.setPlayWhenReady(playWhenReady);
ExoPlayer version 2.9 provides error handling customization via LoadErrorHandlingPolicy.
public final class CustomPolicy
extends DefaultLoadErrorHandlingPolicy {
#Override
public long getRetryDelayMsFor(
int dataType,
long loadDurationMs,
IOException exception,
int errorCount) {
// Replace NoConnectivityException with the corresponding
// exception for the used DataSource.
if (exception instanceof NoConnectivityException) {
return 5000; // Retry every 5 seconds.
} else {
return C.TIME_UNSET; // Anything else is surfaced.
}
}
#Override
public int getMinimumLoadableRetryCount(int dataType) {
return Integer.MAX_VALUE;
}
}
More https://medium.com/google-exoplayer/load-error-handling-in-exoplayer-488ab6908137
playbackPreparer will be called when playButton clicked while player.getPlaybackState() == Player.STATE_IDLE
PlayerControlView.java#L1111
playerView.setPlaybackPreparer {
simpleExoPlayer.prepare(
ExtractorMediaSource.Factory(source).createMediaSource(video),
false,
true
)
}
Create resume fun and do something like this:
player = [your exoPlayer]
fun resumeTrack() {
if (player.playbackError != null) {
player.retry();
}
setPlayWhenReady(true);
exoPlayer.playWhenReady = true
}
You can also do something like :
protected void play() {
if (null != getPlaybackError()) {
retry();
}
setPlayWhenReady(true);
}
Before you call player.play() in your onClick() method, just check if the player has a sourceException in playerError and call player.prepare() before calling play() like this in Kotlin:
player.playerError.takeIf { it?.sourceException is IOException }?.run {
player.prepare()
}