Video customview in full screen exits when toggling the device screen in android - android-webview

I am developing an android application which is purely web based. It needs to display HTML5 based webpages using webview which is rich in multimedia contents. when I try to play video content embedded in the webpage its all gone fine and i am also able to play video in full screen.
But my main problem starts here: When I toggled the device screen while playing fullscreen video, it suddenly exits from playback to its current web page. I am stucked here and here I am including all codes that I used as I can in hopes someone can help me.
Activity that is used: MainActivity.java
`
public class MainActivity extends Activity {
private WebView webView;
private FrameLayout customViewContainer;
private WebChromeClient.CustomViewCallback customViewCallback;
private View mCustomView;
private MyWebViewClient mWebViewClient;
private MyWebChromeClient mWebChromeClient;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
customViewContainer = (FrameLayout) findViewById(R.id.customViewContainer);
webView = (WebView) findViewById(R.id.webView);
mWebViewClient = new MyWebViewClient();
webView.setWebViewClient(mWebViewClient);
mWebChromeClient = new MyWebChromeClient();
webView.setWebChromeClient(mWebChromeClient);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setSaveFormData(true);
webView.loadUrl("http://yuotube.com");
}
public boolean inCustomView() {
return (mCustomView != null);
}
public void hideCustomView() {
mWebChromeClient.onHideCustomView();
}
#Override
protected void onPause() {
super.onPause(); //To change body of overridden methods use File | Settings | File Templates.
webView.onPause();
}
#Override
protected void onResume() {
super.onResume(); //To change body of overridden methods use File | Settings | File Templates.
webView.onResume();
}
#Override
protected void onStop() {
super.onStop(); //To change body of overridden methods use File | Settings | File Templates.
if (inCustomView()) {
hideCustomView();
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (inCustomView()) {
hideCustomView();
return true;
}
if ((mCustomView == null) && webView.canGoBack()) {
webView.goBack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
// WebChromeClient Class
public class MyWebChromeClient extends WebChromeClient {
private Bitmap mDefaultVideoPoster;
private View mVideoProgressView;
#Override
public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
onShowCustomView(view, callback); //To change body of overridden methods use File | Settings | File Templates.
}
#Override
public void onShowCustomView(View view,CustomViewCallback callback) {
// if a view already exists then immediately terminate the new one
if (mCustomView != null) {
callback.onCustomViewHidden();
return;
}
mCustomView = view;
webView.setVisibility(View.GONE);
customViewContainer.setVisibility(View.VISIBLE);
customViewContainer.addView(view);
customViewCallback = callback;
}
#Override
public View getVideoLoadingProgressView() {
if (mVideoProgressView == null) {
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
mVideoProgressView = inflater.inflate(R.layout.video_progress, null);
}
return mVideoProgressView;
}
#Override
public void onHideCustomView() {
super.onHideCustomView(); //To change body of overridden methods use File | Settings | File Templates.
if (mCustomView == null)
return;
webView.setVisibility(View.VISIBLE);
customViewContainer.setVisibility(View.GONE);
// Hide the custom view.
mCustomView.setVisibility(View.GONE);
// Remove the custom view from its container.
customViewContainer.removeView(mCustomView);
customViewCallback.onCustomViewHidden();
mCustomView = null;
}
}
// WebViewClient Class
public class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url); //To change body of overridden methods use File | Settings | File Templates.
}
ProgressDialog dialog = ProgressDialog.show(MainActivity.this, "",
"Loading multimedia! Please wait...", true);
#Override
public void onPageFinished(WebView view, String url) {
dialog.dismiss();
}
}
}
web_activity.xml
<!-- View that will be hidden when video goes fullscreen -->
<RelativeLayout
android:id="#+id/nonVideoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.iptvmodified.VideoEnabledWebView
android:id="#+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<!-- View where the video will be shown when video goes fullscreen -->
<RelativeLayout
android:id="#+id/videoLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- View that will be shown while the fullscreen video loads (maybe include a spinner and a "Loading..." message) -->
<View
android:id="#+id/videoLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="invisible" />
</RelativeLayout>
`activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
I am searching for a solution for thisand googled this to death with no progress made. Any help would be greatly appreciated.

Normally when the device is rotated your activity gets destroyed and recreated. This is probably causing the WebView to get 'kicked out of' fullscreen playback.
You need to override Activity.onConfigurationChanged and declare you want to handle orientation changes in your manifest. See the docs for details.

Related

SupportMapFragmentManagers getMapAsync() does not trigger onMapReady(GoogleMap map)

I have a
public abstract class MyMapFragment implements OnMapReadyCallback
{
//
public GoogleMap googleMap;
SupportMapFragment mapFragment;
#IdRes
public abstract int getSupportMapFragId();
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// http://stackoverflow.com/a/36592000/5102206
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
// Do something for lollipop and above versions
mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(getSupportMapFragId());
} else {
// do something for phones running an SDK before lollipop
mapFragment = (SupportMapFragment) getFragmentManager().findFragmentById(getSupportMapFragId());
}
mapFragment.getMapAsync(this);
}
//..
#Override
public void onMapReady(GoogleMap map) {
this.googleMap = map;
}
}
According to my breakpoints onViewCreated() is called, but onMapReady() is not called (breakpoint on this.googleMap = map not triggered)
On Android 5, 6 and 7 it works fine so far and I can see the Map..
On Android 4.X (API 16 - API 19) devices my app starts up, but then it seem to freeze there... I see a white blank screen.
On Android 4.X OS devices:
1. With getFragmentManager(), the mapFragment object is null after the else condition.
2. With getChildFragmentMenager() the mapfragment seem to be valid and non-null, but onMapReady not triggered.
What am I missing here?
Note: You cannot inflate a layout into a fragment when that layout includes a . Nested fragments are only supported when added to a fragment dynamically
If you want to inflate a map in a fragment you can either do it in xml or do it in java code like this:
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
FragmentManager fm = getChildFragmentManager();
SupportMapFragment mapFragment = (SupportMapFragment) fm.findFragmentByTag("mapFragment");
if (mapFragment == null) {
mapFragment = new SupportMapFragment();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.mapFragmentContainer, mapFragment, "mapFragment");
ft.commit();
fm.executePendingTransactions();
}
mapFragment.getMapAsync(callback);
}
And also the simple container
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapFragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
Also, you don't need to implement the onMapReadyCallback in the class definition. Instead of callback you create a new OnMapReadyCallback() right there:
MapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap mMap) {
googleMap = mMap;
}
});
You also need these
MapView mMapView;
private GoogleMap googleMap;
I hope this helps somehow !
There was an issue with a blocking thread from RxJava on main thread. So it was not an Google Maps issue.
I don't quite understand why you are nesting fragments, specially because it can cause performance issues.
If you take a look at Google Samples, the Google Maps examples uses an Activity and SupportMapFragment:
public class MapsActivityCurrentPlace extends AppCompatActivity
implements OnMapReadyCallback, ConnectionCallbacks,
OnConnectionFailedListener {
#Override
public void onMapReady(GoogleMap map) {
mMap = map;
// Use a custom info window adapter to handle multiple lines of text in the
// info window contents.
mMap.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter() {
#Override
// Return null here, so that getInfoContents() is called next.
public View getInfoWindow(Marker arg0) {
return null;
}
#Override
public View getInfoContents(Marker marker) {
// Inflate the layouts for the info window, title and snippet.
View infoWindow = getLayoutInflater().inflate(R.layout.custom_info_contents,
(FrameLayout)findViewById(R.id.map), false);
TextView title = ((TextView) infoWindow.findViewById(R.id.title));
title.setText(marker.getTitle());
TextView snippet = ((TextView) infoWindow.findViewById(R.id.snippet));
snippet.setText(marker.getSnippet());
return infoWindow;
}
});
// Turn on the My Location layer and the related control on the map.
updateLocationUI();
// Get the current location of the device and set the position of the map.
getDeviceLocation();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mLastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION);
mCameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION);
}
setContentView(R.layout.activity_maps);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */,
this /* OnConnectionFailedListener */)
.addConnectionCallbacks(this)
.addApi(LocationServices.API)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle connectionHint) {
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult result) {
Log.d(TAG, result.getErrorMessage());
}
#Override
public void onConnectionSuspended(int cause) {
Log.d(TAG, "Play services connection suspended");
}
}

How to detect backbutton/home press from a service like messenger chat head service?

I have been looking through several stackoverflow question to find out how can I listen to backpress button on a service using windows manager. Most of the answers proposes that it's not possible, however I can see that messenger handle it very well.
How does messenger handle the backpress button on it's head chat service? (Or I am completely wrong and they are not a service using windows manager?)
To achieve what you need, you need to extend some ViewGroup that you are going to use as a root container for your views. Let's start with this:
public class BackButtonAwareLinearLayout extends LinearLayout {
public interface BackButtonListener {
void onBackButtonPressed();
}
#Nullable
private BackButtonListener mListener;
public BackButtonAwareLinearLayout(Context context) {
super(context);
}
public BackButtonAwareLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BackButtonAwareLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setBackButtonListener(#Nullable BackButtonListener listener) {
mListener = listener;
}
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& mListener != null) {
mListener.onBackButtonPressed();
return true;
}
return super.dispatchKeyEvent(event);
}
}
Basically, overriding dispatchKeyEvent is what does a trick for us here.
Then make a use of it in some xml (I have called it chat_head_container.xml):
<?xml version="1.0" encoding="utf-8"?>
<com.pablo432.myapplication.BackButtonAwareLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="48sp"
android:text="Hello, world!"
android:textColor="#000"
android:background="#f5f5f5"
android:gravity="center"/>
</com.pablo432.myapplication.BackButtonAwareLinearLayout>
Next, create a Service that adds our view to the WindowManager (though I suppose you know how to do it, I'll post it anyway for the sake of completeness):
public class ChatHeadService extends Service
implements BackButtonAwareLinearLayout.BackButtonListener {
private WindowManager mWindowManager;
private BackButtonAwareLinearLayout mRootContainer;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRootContainer = (BackButtonAwareLinearLayout) inflater.inflate(
R.layout.chat_head_container, null, false);
mRootContainer.setBackButtonListener(this);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
PixelFormat.TRANSPARENT);
mWindowManager.addView(mRootContainer, layoutParams);
}
#Override
public void onBackButtonPressed() {
mRootContainer.setBackButtonListener(null);
mWindowManager.removeView(mRootContainer);
}
#Override
public void onDestroy() {
super.onDestroy();
if (mRootContainer != null) mWindowManager.removeView(mRootContainer);
}
}
So long story short, BackButtonAwareLinearLayout exposes a listener interface, that our service needs to implement and subscribe itself to the Layout.
Also have in mind that this addresses handling back button. To handle home button, you may want to take a look at https://stackoverflow.com/a/31340960 and https://stackoverflow.com/a/33580971 - basically my answer is a bit of a summary from those two links + https://stackoverflow.com/a/15980900 but contains few tweaks (like, for example, you can't set FLAG_NOT_FOCUSABLE in WindowManager.LayoutParams).
Of course you need to start your service somewhere by calling startService, declare this service in AndroidManifest.xml and add a SYSTEM_ALERT_WINDOW permission.

togglebutton state changes when listview is scrolled down in android?

i have placed a textview and a toggle button into a list view by using the following xml file and i have used an arrayadapter to set it on to my layout, my problem is when i scroll down in the list view by unchecking a few toggle buttons (which are all set to be checked by default), they go back to default state and few others change state. As i want to fetch the state of the toggle button in my later stages its very important to stick on to the state that ive assigned to the toggle button. Please help to resolve this problem. Here are my xml file and the java code i use to set it on to the adapter. thank you in prior...
xml file that is named listdecor is included here
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/lvlistdecor"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000"
android:orientation="horizontal"
android:weightSum="2" >
<TextView
android:id="#+id/tvlistdecor"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:ellipsize="marquee"
android:singleLine="true"
android:text="sample"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:typeface="sans" />
<CheckBox
android:id="#+id/cbusn_checked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="6sp"
android:layout_weight="0.5"
android:checked="true"
android:focusable="false"
android:padding="10dp" />
</LinearLayout>
Sir i made certain changes and also included the custom adapter to the listview and also changed to CheckBox instead of toggle buttons. but now the entire list view is not visible on my emulator. i've included my edited java class and xml file, please suggest me the changes, thank you...
The modified java code is included here
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // Setting the window to full screen Step 1
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); // Setting the window to full screen Step 2
setContentView(R.layout.attendance5);
facid = (TextView) findViewById(R.id.tvfacid);
facname = (TextView) findViewById(R.id.tvfacname);
subjectchosen = (TextView) findViewById(R.id.tvsubject);
// llattendlist = (LinearLayout)findViewById(R.id.llattendlist);
String Uid = getIntent().getStringExtra("Uid");
facid.setText(Uid);
String Name = getIntent().getStringExtra("Name");
facname.setText(Name);
String Sub = getIntent().getStringExtra("Sub");
subjectchosen.setText(Sub);
Attendusn = getIntent().getExtras();
if(Attendusn!=null)
{
AttendStud_usn = (ArrayList<String>)Attendusn.getStringArrayList("AttendStud_usn");
}
String AttendLen = String.valueOf(AttendStud_usn.size());
//int flag = 0 ;
//subjectchosen.setText(String.valueOf(AttendStud_usn));
myListView = (ListView) findViewById(R.id.lvnames);
// myListView.setAdapter(new ArrayAdapter<String>(this, R.layout.listdecoration,R.id.tvlistdecor, AttendStud_usn));
//ArrayAdapter<String> MyAdapter= new ArrayAdapter<String>(this,R.layout.listdecoration, R.id.tvlistdecor,AttendStud_usn);
//myListView.setAdapter(MyAdapter);
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View item, int position,
long id) {
// TODO Auto-generated method stub
Stud_usn usn= MyAdapter.getItem(position);
usn.toggleChecked();
Stud_usnViewHolder vh_usn= (Stud_usnViewHolder) item.getTag();
vh_usn.getCheckBox().setChecked(usn.isChecked());
}
});
MyAdapter = new Stud_Adapter(this,AttendStud_usn);
myListView.setAdapter(MyAdapter);
}
private static class Stud_usn{
private String usn_no="";
private boolean checked=true;
public Stud_usn(String usn_no){
this.usn_no = usn_no;
}
public String getUsn(){
return usn_no;
}
public void setChecked(boolean checked){
this.checked=checked;
}
public Object isChecked() {
// TODO Auto-generated method stub
return checked;
}
public String toString(){
return usn_no;
}
public void toggleChecked() {
// TODO Auto-generated method stub
checked = !checked;
}
}
private static class Stud_usnViewHolder{
private CheckBox checkbox;
private TextView textview;
public Stud_usnViewHolder(TextView textview, CheckBox checkbox){
this.checkbox=checkbox;
this.textview=textview;
}
public CheckBox getCheckBox() {
// TODO Auto-generated method stub
return checkbox;
}
public TextView getTextView(){
return textview;
}
}
private static class Stud_Adapter extends ArrayAdapter<Stud_usn>{
public LayoutInflater inflater;
public Stud_Adapter(Context context, List<String> attendStud_usn) {
super(context,R.layout.listdecoration,R.id.tvlistdecor);
// TODO Auto-generated constructor stub
inflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent){
Stud_usn usn = (Stud_usn) this.getItem(position);
CheckBox checkbox;
TextView textview;
if(convertView == null){
convertView = inflater.inflate(R.layout.listdecoration,null);
textview = (TextView) convertView.findViewById(R.id.tvlistdecor);
checkbox = (CheckBox) convertView.findViewById(R.id.cbusn_checked);
}
return convertView;
}
}
}
The view objects are recycled. You have to store that info sepperately. Not in the view but in the with the data you use in the adapter. When generating the views use this info to set the button state again.
You have to implement a custom adapter. Use the BaseAdapter class.

onLongClick with Context Action Bar CAB not taking place, only onListItemClick performed, in tablet activity with ListFragment and DetailFragment

I have a ListFragment that is called from a ListActivity, with a dual fragment layout on a tablet.
I have used the standard MasterDetail Fragment setup from Android Studio 0.3.6 API 19.
MyListFragment looks as follows:
import android.app.Activity;
import android.content.ContentUris;
import android.net.Uri;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.support.v4.widget.SimpleCursorAdapter;
import com.supascale.supascale.contentprovider.SupaScaleContentProvider;
import com.supascale.supascale.database.SupascaleDb;
/**
* A list fragment representing a list of Animals. This fragment
* also supports tablet devices by allowing list items to be given an
* 'activated' state upon selection. This helps indicate which item is
* currently being viewed in a {#link AnimalDetailFragment}.
* <p>
* Activities containing this fragment MUST implement the {#link Callbacks}
* interface.
*
* You can create new ones via the ActionBar entry "Insert".
* You can delete existing ones via a long press on the item.
*/
public class AnimalListFragment extends ListFragment implements
android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor>{
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
private SimpleCursorAdapter adapter;
protected Object mActionMode;
private ActionMode.Callback mActionModeCallback;
/**
* The serialization (saved instance state) Bundle key representing the
* activated item position. Only used on tablets.
*/
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
* The fragment's current callback object, which is notified of list item
* clicks.
*/
private Callbacks mCallbacks = sDummyCallbacks;
/**
* The current activated item position. Only used on tablets.
*/
private int mActivatedPosition = ListView.INVALID_POSITION;
/**
* A callback interface that all activities containing this fragment must
* implement. This mechanism allows activities to be notified of item
* selections. NB AJW
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onItemSelected(Uri id);
}
/**
* A dummy implementation of the {#link Callbacks} interface that does
* nothing. Used only when this fragment is not attached to an activity.
*/
private static Callbacks sDummyCallbacks = new Callbacks() {
#Override
public void onItemSelected(Uri animalUri) {
}
};
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public AnimalListFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
return inflater.inflate(R.layout.animal_list, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
String[] from = new String[] { SupascaleDb.KEY_Animal_ANIMALIDENTIFICATION };
// Fields on the UI to which we map
int[] to = new int[] { R.id.label };
getLoaderManager().initLoader(0, null, this);
adapter = new SimpleCursorAdapter(getActivity(), R.layout.animal_row, null, from,
to, 0);
setListAdapter(adapter);
// Restore the previously serialized activated item position.
if (savedInstanceState != null
&& savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
}
mActionModeCallback = new ActionMode.Callback() {
// Called when the action mode is created; startActionMode() was called
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
// Called each time the action mode is shown. Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case DELETE_ID:
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(SupaScaleContentProvider.CONTENT_URI_ANIMAL + "/"
+ info.id);
getActivity().getContentResolver().delete(uri, null, null);
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
// Called when the user exits the action mode
#Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
getView().setOnLongClickListener( new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mActionMode != null) {
return false;
}
// Start the CAB using the ActionMode.Callback defined above
mActionMode = view.startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
#Override
public void onDetach() {
super.onDetach();
// Reset the active callbacks interface to the dummy implementation.
mCallbacks = sDummyCallbacks;
}
#Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
//2013-11-26 AJW Append the clicked item's row ID with the content provider Uri
Uri animalUri = ContentUris.withAppendedId(SupaScaleContentProvider.CONTENT_URI_ANIMAL, id);
// Send the event and Uri to the host activity
mCallbacks.onItemSelected(animalUri);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mActivatedPosition != ListView.INVALID_POSITION) {
// Serialize and persist the activated item position.
outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
}
}
/**
* Turns on activate-on-click mode. When this mode is on, list items will be
* given the 'activated' state when touched.
*/
public void setActivateOnItemClick(boolean activateOnItemClick) {
// When setting CHOICE_MODE_SINGLE, ListView will automatically
// give items the 'activated' state when touched.
getListView().setChoiceMode(activateOnItemClick
? ListView.CHOICE_MODE_SINGLE
: ListView.CHOICE_MODE_NONE);
}
private void setActivatedPosition(int position) {
if (position == ListView.INVALID_POSITION) {
getListView().setItemChecked(mActivatedPosition, false);
} else {
getListView().setItemChecked(position, true);
}
mActivatedPosition = position;
}
/**
* Creates a new loader after the initLoader () call
*/
//#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = { SupascaleDb.KEY_ROWID, SupascaleDb.KEY_Animal_ANIMALIDENTIFICATION };
CursorLoader cursorLoader = new CursorLoader(getActivity(),
SupaScaleContentProvider.CONTENT_URI_ANIMAL, projection, null, null, null);
return cursorLoader;
}
//#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
//#Override
public void onLoaderReset(Loader<Cursor> loader) {
// data is not available anymore, delete reference
adapter.swapCursor(null);
}
}
My AnimalListActivity looks as follows:
/**
* An activity representing a list of Animals. This activity
* has different presentations for handset and tablet-size devices. On
* handsets, the activity presents a list of items, which when touched,
* lead to a {#link AnimalDetailActivity} representing
* item details. On tablets, the activity presents the list of items and
* item details side-by-side using two vertical panes.
* <p>
* The activity makes heavy use of fragments. The list of items is a
* {#link AnimalListFragment} and the item details
* (if present) is a {#link AnimalDetailFragment}.
* <p>
* This activity also implements the required
* {#link AnimalListFragment.Callbacks} interface
* to listen for item selections.
*/
public class AnimalListActivity extends FragmentActivity
implements AnimalListFragment.Callbacks {
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
private SimpleCursorAdapter adapter;
private static final int ACTIVITY_CREATE = 0;
private static final int ACTIVITY_EDIT = 1;
private static final int DELETE_ID = Menu.FIRST + 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animal_list);
if (findViewById(R.id.animal_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((AnimalListFragment) getSupportFragmentManager()
.findFragmentById(R.id.animal_list))
.setActivateOnItemClick(true);
}
// TODO: If exposing deep links into your app, handle intents here.
}
/**
* Callback method from {#link AnimalListFragment.Callbacks}
* indicating that the item with the given ID was selected.
*/
#Override
public void onItemSelected(Uri animalUri) {
//String id = Long.toString(ContentUris.parseId(animalUri));
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(AnimalDetailFragment.ARG_URI, animalUri.toString()); //Key value pair Adrian "animal_uri = .."
AnimalDetailFragment fragment = new AnimalDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.animal_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, AnimalDetailActivity.class);
//Uri todoUri = Uri.parse(SupaScaleContentProvider.CONTENT_URI_ANIMAL + "/" + id);
//detailIntent.putExtra(AnimalDetailFragment.ARG_ITEM_ID, id);
//startActivity(detailIntent);
detailIntent.putExtra(SupaScaleContentProvider.CONTENT_ITEM_TYPE, animalUri);
// Activity returns an result if called with startActivityForResult
startActivityForResult(detailIntent, ACTIVITY_EDIT);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.listmenu, menu);
return true;
}
// Reaction to the menu selection
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.insert:
createAnimal();
return true;
}
return super.onOptionsItemSelected(item);
}
private void createAnimal() {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
//arguments.putString(AnimalDetailFragment.ARG_URI, animalUri.toString()); //Key value pair Adrian "animal_uri = .."
AnimalDetailFragment fragment = new AnimalDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.animal_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
Intent i = new Intent(this, AnimalDetailActivity.class);
startActivityForResult(i, ACTIVITY_EDIT);
}
}
}
AnimalListActivity.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/animal_list"
android:name="com.supascale.supascale.AnimalListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:context=".AnimalListActivity"
tools:layout="#android:layout/list_content" />
The List.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#android:id/list" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Animal Data"
android:id="#android:id/empty" />
</LinearLayout>
The row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/icon"
android:layout_width="30dp"
android:layout_height="24dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/reminder" >
</ImageView>
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:lines="1"
android:text="#+id/textViewNormalTotal"
android:textSize="24sp"
>
</TextView>
</LinearLayout>
The fragment list scrolls correctly, and when clicked the detail fragment is correctly populated. When I long click, nothing happens, and when I release the longclick, the click event is fired.
I need for the long click to result in the CAB (Context Action Bar) to display, so that I can delete a record, or share a record etc.
mActionMode = view.startActionMode(mActionModeCallback); just never appears to be called.
Hope I have been clear enough.
Regards
Adrian
I edited and added the imports for the ListFragment, as it may have to do with support.v4?
Ok, so I did not understand the CAB help so nicely.
http://developer.android.com/guide/topics/ui/menus.html#CAB
For a ListView or ListFragment, and not another kind of view, you must use the setOnItemLongClickListener,
and not the setOnLongClickListener, as in the help from developer.android.com.
The setOnLongClickListener, is never activated by the listview, and thus never can procede to create the CAB.
I removed the following code:
getView().setOnLongClickListener( new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
if (mActionMode != null) {
return false;
}
// Start the CAB using the ActionMode.Callback defined above
mActionMode = view.startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
and replaced with the following:
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> arg0, View view,
int position, long id) {
if (mActionMode != null) {
return false;
}
Animal_id = id;
// Start the CAB using the ActionMode.Callback defined above
mActionMode = getActivity().startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
Now the long click event fires, and I can get the CAB to display properly.

DialogFragments and activities, passing info

I'm trying to show a DialogFragment asking the user his name, and I need to get back this string to my main activity.
I've created an custom dialog fragment following the android guide: http://developer.android.com/guide/topics/ui/dialogs.html#CustomLayout
In my code, in response to the user click on positive button, I am able to call a function in my main activity:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
builder.setView(inflater.inflate(R.layout.textdialog, null)).setPositiveButton("OK", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int id) {
((mainActivity)getActivity()).doPositiveClick();
}
}).setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
((mainActivity)getActivity()).doNegativeClick();
}
});
return builder.create();
}
where R.layout.textdialog is:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="#+id/textdraw"
android:inputType="textEmailAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="introduce text" />
</LinearLayout>
My question is, how could I get the text written by the user in my dialog fragment and send it back to my main activity?
thanks!
Well, I solved myself (but not sure if it's the better way)
I've done some changes in my onCreateDialog.
Save the view in a variable:
View v= inflater.inflate(R.layout.textdialog, null);
Save the edittext in a variable:
final EditText ed=(EditText)v.findViewById(R.id.textdraw);
Send back the value in edittext variable when the user clicks the positive button:
getActivity()).doPositiveClick(ed.getText().toString());
Full code is here:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View v= inflater.inflate(R.layout.textdialog, null);
// reference to the edittext
final EditText ed= (EditText)v.findViewById(R.id.textdraw);
builder.setView(v).setPositiveButton("OK", new DialogInterface.OnClickListener(){
#Override
public void onClick(DialogInterface dialog, int id) {
((mainActivity)getActivity()).doPositiveClick(ed.getText().toString());
}
}).setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
((mainActivity)getActivity()).doNegativeClick();
}
});
return builder.create();
}
and the code in main activity:
public void doPositiveClick(String ed){
Toast.makeText(this, "Hi, "+ed, Toast.LENGTH_SHORT).show();
}