I'm trying to use a SupportFragment Google Map with a navigation drawer, initializing the map every two seconds with new overlays pulled from a server. I should also mention that the google maps supportfragment is the default supportfragment, so it is already supposed to be open when the app starts. The problem is that the app is very slow and unresponsive when I run it, prompting my phone to open a dialog asking to close the app because it is unresponsive. I am assuming that it may be getMapAsync(), but that is just a very broad guess. I'm trying to fix the problem to make the app just as responsive when it was a regular fragment and without a navigationdrawer. Here is the code for some of the files that are relevant with the problem. If you need any other information or any other files, feel free to ask.
MainActivity.java:
package com.main.main;
import android.app.FragmentManager;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.PolygonOptions;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutionException;
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, OnMapReadyCallback {
private SupportMapFragment sMapFragment;
private android.support.v4.app.FragmentManager sFm;
private GoogleMap mMap;
List<ParkingSpot> list; //list of parking spots
private static final LatLng DEFAULT_ZOOM_IN = new LatLng(40, -120); //default zoom-in of map
public static final String IP_ADDRESS = "some ip";
public static final String DATABASE = "some db";
public static final String USER_USERNAME = "something";
public static final String USER_PASSWORD = "something";
public LatLngBounds bounds;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sMapFragment = SupportMapFragment.newInstance();
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
if(bounds == null) {
sFm = getSupportFragmentManager();
sFm.beginTransaction().remove(sMapFragment).commit();
sFm.beginTransaction().add(R.id.map, sMapFragment).commit();
}
try {
Object result = new LongOperation().execute().get();
} catch (InterruptedException e) {
throw new NullPointerException("INTERRUPTED EXCEPTION");
} catch (ExecutionException e) {
throw new NullPointerException("EXECUTION EXCEPTION");
}
}
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
FragmentManager fm = getFragmentManager();
int id = item.getItemId();
sFm.beginTransaction().hide(sMapFragment).commitAllowingStateLoss();
if (id == R.id.nav_map) {
Toast.makeText(MainActivity.this, "MAP CLICKED!", Toast.LENGTH_SHORT);
sFm.beginTransaction().show(sMapFragment).commitAllowingStateLoss();
} else if (id == R.id.nav_history) {
Toast.makeText(MainActivity.this, "HISTORY CLICKED!", Toast.LENGTH_SHORT);
} else if (id == R.id.nav_notifications) {
Toast.makeText(MainActivity.this, "NOTIFICATIONS CLICKED!", Toast.LENGTH_SHORT);
} else if (id == R.id.nav_help) {
Toast.makeText(MainActivity.this, "HELP CLICKED!", Toast.LENGTH_SHORT);
} else if (id == R.id.nav_settings) {
Toast.makeText(MainActivity.this, "SETTINGS CLICKED!", Toast.LENGTH_SHORT);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onMapReady(GoogleMap googleMap) {
try {
Object result = new LongOperation().execute().get();
mMap = googleMap;
mMap.clear();
if (bounds == null) { //only completes first time when bounds == null
CameraPosition cameraPosition = new CameraPosition.Builder() //zooms in camera on default location
.target(DEFAULT_ZOOM_IN) //location (LatLng)
.zoom(18) //zoom integer
.build(); //builds camera
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); //animates camera to spot
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); //sets map to hybrid type
}
bounds = mMap.getProjection().getVisibleRegion().latLngBounds; //gets latlng for next query based on camera spot
PolygonOptions rectOptions;
for (ParkingSpot spot : list) { //loops through ArrayList of Parking Spots, adding them to map
rectOptions = new PolygonOptions()
.addAll(spot.getCoordinates())
.clickable(true); //adds all coordinates to map
if (spot.getStatus() == 'F') { //see ParkingSpot.java for char definitions/explanations
rectOptions.fillColor(Color.RED);
} else if (spot.getStatus() == 'T') {
rectOptions.fillColor(Color.GREEN);
} else if (spot.getStatus() == 'H') {
rectOptions.fillColor(Color.YELLOW);
}
Polygon polygon = mMap.addPolygon(rectOptions); //adds rectangle to map
spot.setPolygonId(polygon.getId());
}
mMap.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
#Override
public void onPolygonClick(Polygon polygon) {
Toast.makeText(MainActivity.this, "You clicked on polygon " + getInfoById(polygon.getId()), Toast.LENGTH_SHORT).show();
}
});
mMap.setMyLocationEnabled(true);
mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {
#Override
public boolean onMyLocationButtonClick() {
return false;
}
});
} catch (SecurityException e) {
throw new NullPointerException("SECURITY EXCEPTION");
} catch (InterruptedException e) {
throw new NullPointerException("INTERRUPTED EXCEPTION");
} catch (ExecutionException e) {
throw new NullPointerException("EXECUTION EXCEPTION");
}
}
public String getInfoById(String id) {
for (ParkingSpot spot : list) {
if (spot.getPolygonId().equals(id)) {
return spot.toString();
}
}
return "-1";
}
private class LongOperation extends AsyncTask<String, Void, List> { //main network operation to initialize list
#Override
protected List<ParkingSpot> doInBackground(String... params) {
list = new ArrayList<ParkingSpot>(); //initializes list to ArrayList
String url = "jdbc:mysql://" + IP_ADDRESS + "/" + DATABASE; //creates URL for MySQL query
try {
Class.forName("com.mysql.jdbc.Driver"); //Driver for MySQL query
Connection con = DriverManager.getConnection(url, USER_USERNAME, USER_PASSWORD); //Starts connection to server
PreparedStatement pst; //initializes PreparedStatement outside to allow scope
if (bounds == null) { //if requesting first time
pst = con.prepareStatement("select * from sample_parking_spots");
} else { //if requesting later than first time;
String statement = "select * from sample_parking_spots where coord1lat>=" + bounds.southwest.latitude + " and coord3lat<=" + bounds.northeast.latitude + " and coord3lon>=" + bounds.southwest.longitude + " and coord1lon<=" + bounds.northeast.longitude;
pst = con.prepareStatement(statement);
}
ResultSet rs = pst.executeQuery(); //gets information from server
while (rs.next()) {
String uid = "U" + rs.getString("id"); //gets UID
String coordinate1Lat = rs.getString("coord1lat"); //gets top left latitude (x)
String coordinate1Lon = rs.getString("coord1lon"); //gets top left longitude (y)
String coordinate3Lat = rs.getString("coord3lat"); //gets bottom right latitude (x)
String coordinate3Lon = rs.getString("coord3lon"); //gets bottom right longitude (y)
char status = rs.getString("status").charAt(0); //get status as char
list.add(new ParkingSpot(uid, status, coordinate1Lat, coordinate1Lon, coordinate3Lat, coordinate3Lon)); //adds new ParkingSpot object to ArrayList
}
return list;
} catch (RuntimeException e) {
throw new NullPointerException("SOME OTHER RUNTIME EXCEPTION");
//Json parser can't find some variable or UID or NullPointerException
} catch (SQLException e) {
throw new NullPointerException("SQL EXCEPTION");
//if credentials for SQL are bad or something is wrong with the server
} catch (ClassNotFoundException e) {
throw new NullPointerException("CLASS NOT FOUND EXCEPTION");
//if cannot find SQL driver
}
}
#Override
protected void onPostExecute(List result) { //called after doInBackground completes
super.onPostExecute(list);
list = result; //sets public list to List initialized from database
Timer timer = new Timer("updateMapAvailTask");
TimerTask myTask = new TimerTask() { //creates new task to execute again after timer
#Override
public void run() {
try {
Object result = new LongOperation().execute().get(); //second network thread to download data and initialize map
//gets object so ENTIRE LongOperation thread completes
} catch (InterruptedException e) {
throw new NullPointerException("INTERRUPTED EXCEPTION");
} catch (ExecutionException e) {
throw new NullPointerException("EXECUTION EXCEPTION");
}
}
};
sMapFragment.getMapAsync(MainActivity.this);
timer.schedule(myTask, 2000); //schedules for server pull to happen every couple of seconds
}
}
}
content_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="com.main.main.MainActivity"
tools:showIn="#layout/app_bar_main">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
<FrameLayout
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
activity_main_drawer.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="#+id/nav_map"
android:icon="#drawable/ic_map"
android:title="Map" />
<item
android:id="#+id/nav_history"
android:icon="#drawable/ic_search_history"
android:title="History" />
<item
android:id="#+id/nav_notifications"
android:icon="#drawable/ic_error"
android:title="Notifications" />
</group>
<item android:title="User">
<menu>
<item
android:id="#+id/nav_help"
android:icon="#drawable/ic_help"
android:title="Help" />
<item
android:id="#+id/nav_settings"
android:icon="#drawable/ic_settings"
android:title="Settings" />
</menu>
</item>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.main.main">
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
android:name="android.support.multidex.MultiDexApplication">
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
You AsyncTask actually is blocking the UI thread.
The AsyncTask.get() method is a blocking call, you should just do AsyncTask.execute(). In your case, replace new LongOperation().execute().get() with just new LongOperation().execute().
Related
I'm working on a university project for blind people who use Android phones (a Braille Keyboard) .
So i need disable talkback when user use the keyboard and then enable it again. (for using multitouch on the screen). So I created a new sample simple project for android 8.0, empty activity and I added a couple things.
My sample android manifest:
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
And my simple MainActivity:
package com.jaime.prueba;
import android.os.Bundle;
import android.provider.Settings;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
updateTalkBackState(true);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
private static final String TALKBACK_SERVICE_NAME = "com.google.android.marvin.talkback/.TalkBackService";
private void updateTalkBackState(boolean enableTalkBack) {
if (enableTalkBack) {
enableAccessibilityService(TALKBACK_SERVICE_NAME);
} else {
disableAccessibilityServices();
}
}
private void enableAccessibilityService(String name) {
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, name);
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "1");
}
private void disableAccessibilityServices() {
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "0");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
So...
Firstly: I checked this answer: https://stackoverflow.com/a/44461236/10802519
// It's not possible to turn TalkBack on or off from within your app unless you have been granted the system permission WRITE_SECURE_SETTINGS (via ADB) by the user.
private static final String TALKBACK_SERVICE_NAME = "com.google.android.marvin.talkback/.TalkBackService";
private void updateTalkBackState(boolean enableTalkBack) {
if (enableTalkBack) {
enableAccessibilityService(TALKBACK_SERVICE_NAME);
} else {
disableAccessibilityServices();
}
}
private void enableAccessibilityService(String name) {
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, name);
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, VALUE_ENABLED);
}
private void disableAccessibilityServices() {
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, VALUE_DISABLED);
}
Secondly: In my Terminal I typed:
adb shell pm grant com.jaime.prueba android.permission.WRITE_SECURE_SETTINGS
Thirdly: But I got the following:
java.lang.SecurityException: Package com.jaime.prueba has not requested permission android.permission.WRITE_SECURE_SETTINGS
at com.android.server.pm.permission.BasePermission.enforceDeclaredUsedAndRuntimeOrDevelopment(BasePermission.java:379)
at com.android.server.pm.permission.PermissionManagerService.grantRuntimePermission(PermissionManagerService.java:1404)
at com.android.server.pm.permission.PermissionManagerService.access$900(PermissionManagerService.java:89)
at com.android.server.pm.permission.PermissionManagerService$PermissionManagerInternalImpl.grantRuntimePermission(PermissionManagerService.java:2093)
at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5411)
at com.android.server.pm.PackageManagerShellCommand.runGrantRevokePermission(PackageManagerShellCommand.java:1730)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:217)
at android.os.ShellCommand.exec(ShellCommand.java:103)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21330)
at android.os.Binder.shellCommand(Binder.java:634)
at android.os.Binder.onTransact(Binder.java:532)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2821)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3856)
at android.os.Binder.execTransact(Binder.java:731)
Fourthly I build my app on Android Studio, Everything fine, nothing red, so I run the app on my phone... and my app does not open, it does not work.
I hope my question is understandable. Let me know, if my questions need something more -First question, and in foreign language- Thank you!
Notice in manifest file:
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
but in terminal you input:
adb shell pm grant com.jaime.prueba android.permission.WRITE_SECURE_SETTINGS
WRITE_SETTINGS and WRITE_SECURE_SETTINGS are two different permissions.
and about WRITE_SECURE_SETTINGS, you could read this post.
I tested my app setting the device in airplane mode, and it crashed.
MoviesListFragment.java
package com.example.android.popularmoviesstage_1;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
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.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* A Class that extends Fragment to implement the Movie List structure
* A Fragment represents a behavior or a portion of user interface in a FragmentActivity.
*/
public class MoviesListFragment extends Fragment{
public static ProgressBar mLoadingIndicator;
public static TextView mErrorMessageDisplay;
public static SwipeRefreshLayout mSwipeContainer;
public static PosterAdapter mMoviesAdapter;
private Context mContext;
private MoviesRecyclerView mScrollListener;
private int mPage;
private int mSorting;
private static final int SORTING_POPULAR = 1;
private static final int SORTING_RATED = 2;
private static final String BUNDLE_MOVIES_KEY = "movieList";
private static final String BUNDLE_PAGE_KEY = "currentPage";
private static final String BUNDLE_SORTING_KEY = "currentSorting";
private static final String BUNDLE_ERROR_KEY = "errorShown";
private static final String TAG = MoviesListFragment.class.getSimpleName();
#Override
public void onCreate(#Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true); //Allowing menu options in the ActionBar
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState){
Boolean errorShown = false;
if (savedInstanceState != null){
errorShown = savedInstanceState.getBoolean(BUNDLE_ERROR_KEY);
}
if (savedInstanceState != null && !errorShown){
mPage = savedInstanceState.getInt(BUNDLE_PAGE_KEY);
mSorting = savedInstanceState.getInt(BUNDLE_SORTING_KEY);
} else {
mPage = 1;
mSorting = 1;
}
//inflating the movies in this fragment
View rootView = inflater.inflate(R.layout.movie_list_fragment, container, false);
mContext = getContext();
final int columns = getResources().getInteger(R.integer.grid_columns);
// Laying the movie items in grid formation.
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, columns, GridLayoutManager.VERTICAL, false);
RecyclerView recyclerView = rootView.findViewById(R.id.rv_posters);
recyclerView.setLayoutManager(gridLayoutManager);
//setting the size for all movie items
recyclerView.setHasFixedSize(true);
mMoviesAdapter = new PosterAdapter();
recyclerView.setAdapter(mMoviesAdapter);
//progress indicator catching movie data from the internet
mLoadingIndicator = rootView.findViewById(R.id.pb_loading_indicator);
mScrollListener = new MoviesRecyclerView(gridLayoutManager, mPage) {
#Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
Log.d(TAG, "Loading page: " + String.valueOf(page));
mPage = page;
loadCards(mSorting);
}
};
recyclerView.addOnScrollListener(mScrollListener);
//The SwipeRefreshLayout is used whenever the user refresh the contents of a view via a vertical swipe gesture.
mSwipeContainer = rootView.findViewById(R.id.sr_swipe_container);
mSwipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mErrorMessageDisplay.setVisibility(View.INVISIBLE);
clearGridView();
loadCards(mSorting);
}
});
mSwipeContainer.setColorSchemeResources(R.color.colorAccent);
mErrorMessageDisplay = rootView.findViewById(R.id.tv_error_message_display);
if (savedInstanceState != null && !errorShown){
ArrayList<Movie> movieArrayList = savedInstanceState.getParcelableArrayList(BUNDLE_MOVIES_KEY);
mMoviesAdapter.setMoviesData(movieArrayList);
} else {
loadCards(mSorting);
}
return rootView;
}
//onSaveInstanceState() is called before your activity is paused.
// So any info that it needs after it is potentially destroyed can be retrieved from the saved Bundle
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
List<Movie> movieList = mMoviesAdapter.getMoviesData();
if (movieList != null){
ArrayList<Movie> movieArrayList = new ArrayList<>(mMoviesAdapter.getMoviesData());
outState.putParcelableArrayList(BUNDLE_MOVIES_KEY, movieArrayList);
outState.putInt(BUNDLE_PAGE_KEY, mPage);
outState.putInt(BUNDLE_SORTING_KEY, mSorting);
} else {
if (mErrorMessageDisplay.isShown()){
outState.putBoolean(BUNDLE_ERROR_KEY, true);
}
}
}
/**
* A method that invokes the AsyncTask to populate the RecyclerView,
* it's based on the sorting option selected by the user. Default is "popular movies"
*
* #param sorting the way of sorting selected by the user
*/
private void loadCards(int sorting){
if(NetworkUtils.isOnline(mContext)){
String method;
switch (sorting){
case SORTING_POPULAR:
method = NetworkUtils.getMoviesPopular();
break;
case SORTING_RATED:
method = NetworkUtils.getMoviesTopRated();
break;
default:
method = NetworkUtils.getMoviesPopular();
break;
}
String[] posters = new String[]{method, String.valueOf(mPage)};
new FetchMovieTask().execute(posters);
} else {
showErrorMessage(R.string.error_no_connectivity);
if (mSwipeContainer.isRefreshing()) {
mSwipeContainer.setRefreshing(false);
}
}
}
/**
* Reset the GridView properties and adapter
*/
private void clearGridView(){
mScrollListener.resetState();
mPage = 1;
mMoviesAdapter.clear();
}
/**
* Display the specific error message in the TextView
*
* #param messageId the resource id of the error string
*/
public static void showErrorMessage(int messageId){
mErrorMessageDisplay.setText(Resources.getSystem().getText(messageId));
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
//onCreateOptionsMenu() to specify the options menu for an activity.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
switch (mSorting) {
case SORTING_POPULAR:
menu.findItem(R.id.sort_popular).setChecked(true);
break;
case SORTING_RATED:
menu.findItem(R.id.sort_rated).setChecked(true);
break;
default:
menu.findItem(R.id.sort_popular).setChecked(true);
break;
}
}
/*
*When the user selects an item from the options menu (including action items in the app bar),
* the system calls our activity's onOptionsItemSelected() method.
* This method passes the MenuItem selected. We can identify the item by calling getItemId(),
* which returns the unique ID for the menu item (defined by the android:id attribute in the menu
* resource or with an integer given to the add() method). We can match this ID against known menu
* items to perform the appropriate action.
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.sort_popular || id == R.id.sort_rated) {
if (!item.isChecked()) {
mSorting = item.getOrder();
item.setChecked(true);
clearGridView();
loadCards(mSorting);
}
return true;
}
return super.onOptionsItemSelected(item);
}
}
MainActivity.java
package com.example.android.popularmoviesstage_1;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
/**
* MainActivity, which is presented to the user when the app is launched.
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//MoviesListFragment is used here
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.android.popularmoviesstage_1.MainActivity">
<!-- This fragment is used in MainActivity -->
<fragment
android:name="com.example.android.popularmoviesstage_1.MoviesListFragment"
android:id="#+id/f_movie_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/movie_list_fragment" />
</FrameLayout>
movie_list_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/padding_ten"
android:paddingRight="#dimen/padding_ten">
<TextView
android:id="#+id/tv_error_message_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="#dimen/padding_sixteen"
android:text="#string/error_message"
android:drawableRight="#android:drawable/stat_notify_error"
android:drawableEnd="#android:drawable/stat_notify_error"
android:drawableTint="#android:color/holo_red_dark"
android:drawablePadding="#dimen/padding_four"
android:textSize="#dimen/movie_detail_text_size"
android:background="#android:color/white"
android:visibility="invisible" />
<ProgressBar
android:id="#+id/pb_loading_indicator"
android:layout_width="#dimen/padding_fourty_two"
android:layout_height="#dimen/padding_fourty_two"
android:layout_gravity="center"
android:indeterminateTint="#android:color/holo_blue_bright"
android:visibility="invisible" />
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/sr_swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/rv_posters"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
strings.xml
<resources>
<string name="app_name">Popular Movies</string>
<string name="sort_popular">Most Popular</string>
<string name="sort_rated">Top Rated</string>
<string name="poster_image_alt">Poster Image</string>
<string name="error_message">An error has occurred. Please try again by swiping down</string>
<string name="error_no_connectivity">Your device is not connected to the Internet.</string>
<string name="error_moviedb_list">Error from the MovieDB Service. Swipe down to try again</string>
<string name="error_movie_poster">Error loading the poster, sorry!</string>
<string name="no_internet_connection">No Internet connection</string>
<string name="button_retry">RETRY</string>
<string name="checkInternetConnection">Check Internet</string>
</resources>
This is the error description:
FATAL EXCEPTION: main
Process: com.example.android.popularmoviesstage_1, PID: 21294
android.content.res.Resources$NotFoundException: String resource ID #0x7f0d0026
Here is the stacktrace of this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.popularmoviesstage_1, PID: 21294
android.content.res.Resources$NotFoundException: String resource ID #0x7f0d0026
at android.content.res.Resources.getText(Resources.java:331)
at com.example.android.popularmoviesstage_1.MoviesListFragment.showErrorMessage(MoviesListFragment.java:185)
at com.example.android.popularmoviesstage_1.MoviesListFragment.loadCards(MoviesListFragment.java:161)
at com.example.android.popularmoviesstage_1.MoviesListFragment.access$300(MoviesListFragment.java:28)
at com.example.android.popularmoviesstage_1.MoviesListFragment$2.onRefresh(MoviesListFragment.java:104)
at android.support.v4.widget.SwipeRefreshLayout$1.onAnimationEnd(SwipeRefreshLayout.java:188)
at android.support.v4.widget.CircleImageView.onAnimationEnd(CircleImageView.java:106)
at android.view.ViewGroup.finishAnimatingView(ViewGroup.java:6278)
at android.view.View.draw(View.java:17027)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.draw(View.java:17086)
at android.view.View.updateDisplayListIfDirty(View.java:16065)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.updateDisplayListIfDirty(View.java:16060)
at android.view.View.draw(View.java:16849)
at android.view.ViewGroup.drawChild(ViewGroup.java:3768)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3554)
at android.view.View.draw(View.java:17086)
at com.android.internal.policy.DecorView.draw(DecorView.java:751)
at android.view.View.updateDisplayListIfDirty(View.java:16065)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:657)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:663)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:771)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2808)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2616)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2223)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1258)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6348)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
at android.view.Choreographer.doCallbacks(Choreographer.java:683)
at android.view.Choreographer.doFrame(Choreographer.java:619)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
In order to get a String in a fragment use the following code
getResources().getString(R.id.error_no_connectivity).
This will fix the crash for you. You are not referencing the string in your strings.xml correctly
You method should be (Removed the static keyword)
public void showErrorMessage(int messageId){
mErrorMessageDisplay.setText(getResources().getString(messageId));
mErrorMessageDisplay.setVisibility(View.VISIBLE);
}
I've made an app, that is discovering nearby bluetooth devices and etc. I can get bluetooth adapter, open-close processes, get paired devices, discovering process. Everthing is fine but discovery process isn't working properly.
I tested my app on 5 different real devices. At the moment I realized I have a problem on Android version but I'm not sure about that.
My app's discovery process is working on,
Huawei P7 - Android Version: 4.4.2
Sony Xperia Z - Android Version: 5.1.1
Samsung Galaxy J3 - Android Version: 5.1.1
My app's discovery process isn't working on,
Samsung Galaxy S7 - Android Version: 6.0.1
Samsung Galaxy J7 - Android Version: 6.0.1
MainActivity.java
package com.sphinxlike.bluetoothexample;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import static java.lang.System.out;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
/* DECLERATIONS */
// Button
Button openBtn = null; // Open button
Button closeBtn = null; // Close button
Button listBtn = null; // Paired devices list button
Button searchBtn = null; // Discovery new devices button
// Bluetooth
private BluetoothAdapter mBluetoothAdapter = null; // Local bluetooth adapter variable
private static final int REQUEST_ENABLE_BT = 1; // Bluetooth open intent variable
String mDeviceName = null;
String mDeviceAddress = null;
public static String EXTRA_ADDRESS = "device_address";
// XML
TextView tvDeviceName = null; // To show local bluetooth device's name
TextView tvBluetoothState = null; // To show bluetooth state
ListView lvDeviceList = null; // To list paired devices or discovered devices
// Set, List, Array Adapter
private Set<BluetoothDevice> mPairedDevicesSet = null; // Paired devices Set
private Set<BluetoothDevice> mNewDevicesSet = null; // New devices Set
ArrayAdapter mPairedDevicesAdapter = null; // Paired devices array adapter
ArrayAdapter mNewDevicesAdapter = null; // New devices array adapter
ArrayList bluetoothDeviceList = null; // Listing bluetooth devices
private ArrayList<BluetoothDevice> btDeviceList = new ArrayList<BluetoothDevice>();
// Bluetooth State Updater
private Handler mHandler; // Loop in UI
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializes UI elements
tvDeviceName = (TextView)findViewById(R.id.deviceName);
tvBluetoothState = (TextView)findViewById(R.id.bluetoothState);
openBtn = (Button)findViewById(R.id.openBluetooth);
closeBtn = (Button)findViewById(R.id.closeBluetooth);
listBtn = (Button)findViewById(R.id.listBluetooth);
searchBtn = (Button)findViewById(R.id.searchBluetooth);
lvDeviceList = (ListView)findViewById(R.id.deviceList);
// Bluetooth adapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// Button checks
openBtn.setOnClickListener((View.OnClickListener) this);
closeBtn.setOnClickListener((View.OnClickListener) this);
listBtn.setOnClickListener((View.OnClickListener) this);
searchBtn.setOnClickListener((View.OnClickListener) this);
// To update bluetooth state text view
tvBluetoothState.setText(String.valueOf(mBluetoothAdapter.getState()));
// If the phone does not support bluetooth adapter
if (mBluetoothAdapter == null) {
tvBluetoothState.setText("Your phone has not Bluetooth Adapter");
openBtn.setEnabled(false);
closeBtn.setEnabled(false);
listBtn.setEnabled(false);
searchBtn.setEnabled(false);
}
// Updater loop
mHandler = new Handler();
mHandler.post(mUpdate);
}
// Updater Loop
private Runnable mUpdate = new Runnable() {
public void run() {
getBluetoothState();
mHandler.postDelayed(this, 500);
}
};
// Button onClick method
public void onClick(View v) {
switch (v.getId()) {
case R.id.openBluetooth:
if (!mBluetoothAdapter.isEnabled()) {
Intent bluetoothAcIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(bluetoothAcIntent, REQUEST_ENABLE_BT);
} else {
Toast.makeText(getApplicationContext(), "Bluetooth is already open", Toast.LENGTH_SHORT).show();
}
break;
case R.id.closeBluetooth:
if (mBluetoothAdapter.isEnabled()) {
mBluetoothAdapter.disable();
} else {
Toast.makeText(getApplicationContext(), "Bluetooth is already close", Toast.LENGTH_SHORT).show();
}
break;
case R.id.listBluetooth:
pairedDevicesList();
break;
case R.id.searchBluetooth:
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mBroadcastReceiver, filter);
mBluetoothAdapter.startDiscovery();
break;
}
};
#Override
public void onDestroy() {
unregisterReceiver(mBroadcastReceiver);
super.onDestroy();
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// Whenever a remote Bluetooth device is found
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Toast.makeText(getApplicationContext(), device.getName() + ":" + device.getAddress(), Toast.LENGTH_LONG).show();
// lvDeviceList.setAdapter(mPairedDevicesAdapter); // Set list view elements with adapter elements
}
}
};
// Show bluetooth state on UI
public void getBluetoothState() {
tvDeviceName.setText("Device Name: " + mBluetoothAdapter.getName());
switch(mBluetoothAdapter.getState()) { // Set bluetooth state text view
case 10: // STATE_OFF
tvBluetoothState.setText("Bluetooth is closed");
bluetoothDeviceList = new ArrayList(); // Initialize global array list variable that is declared
mPairedDevicesAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, bluetoothDeviceList); // Get array list and use in array adapter
lvDeviceList.setAdapter(mPairedDevicesAdapter); // Set list view elements with adapter elements
break;
case 11: // STATE_TURNING_ON
tvBluetoothState.setText("Bluetooth is opening");
break;
case 12: // STATE_ON
tvBluetoothState.setText("Bluetooth is opened");
break;
case 13: // STATE_TURNING_OFF
tvBluetoothState.setText("Bluetooth is closing");
break;
}
// If bluetooth adapter -> Set buttons
if (mBluetoothAdapter.isEnabled()) {
// Enable listing button
listBtn.setEnabled(true);
searchBtn.setEnabled(true);
} else {
// Disable listing button
listBtn.setEnabled(false);
searchBtn.setEnabled(false);
}
}
// Get paired device list and adapt to list view
public void pairedDevicesList()
{
if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(getApplicationContext(), "Open the Bluetooth", Toast.LENGTH_SHORT).show();
} else {
mPairedDevicesSet = mBluetoothAdapter.getBondedDevices(); // Get paired devices
bluetoothDeviceList = new ArrayList(); // Initialize global array list variable that is declared
if (mPairedDevicesSet.size()>0) {
for (BluetoothDevice bt : mPairedDevicesSet) { // for-each loop
mDeviceName = bt.getName();
mDeviceAddress = bt.getAddress();
bluetoothDeviceList.add(mDeviceName + "\n" + mDeviceAddress); // get the device name and add to array list object
}
} else if (mPairedDevicesSet.size()<=0){
Toast.makeText(getApplicationContext(), "No paired devices", Toast.LENGTH_SHORT).show();
}
mPairedDevicesAdapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, bluetoothDeviceList); // Get array list and use in array adapter
lvDeviceList.setAdapter(mPairedDevicesAdapter); // Set list view elements with adapter elements
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
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="com.sphinxlike.bluetoothexample.MainActivity"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Device Name"
android:id="#+id/deviceName"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp">
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="#+id/openBluetooth"
android:text="B. Open"
android:textAllCaps="false"
android:layout_gravity="center"
android:gravity="center"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="#+id/closeBluetooth"
android:text="B. Close"
android:textAllCaps="false"
android:layout_gravity="center"
android:gravity="center"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="#+id/listBluetooth"
android:text="List Paired"
android:textAllCaps="false"
android:layout_gravity="center"
android:gravity="center"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="#+id/searchBluetooth"
android:text="Search"
android:textAllCaps="false"
android:layout_gravity="center"
android:gravity="center"/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="Bluetooth State"
android:id="#+id/bluetoothState"
android:layout_gravity="center"
android:gravity="center"/>
<ListView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/deviceList">
</ListView>
</LinearLayout>
Firstly, thanks #Tomasz Czura. I solved problem with multiple runtime permission request.
Android Developer 6.0 Changes page says:
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions:
So, I added permission to AndroidManifest.XML:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
then I rearranged my activity like this:
public class MainActivity extends AppCompatActivity {
...
int ACTION_REQUEST_MULTIPLE_PERMISSION = 1; // Any number
#Override
protected void onCreate(Bundle savedInstanceState) {
...
int pCheck = this.checkSelfPermission("Manifest.permission.ACCESS_FINE_LOCATION");
pCheck += this.checkSelfPermission("Manifest.permission.ACCESS_COARSE_LOCATION");
pCheck += this.checkSelfPermission("Manifest.permission.BLUETOOTH_ADMIN");
pCheck += this.checkSelfPermission("Manifest.permission.BLUETOOTH");
if (pCheck != 0) {
this.requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH}, ACTION_REQUEST_MULTIPLE_PERMISSION);
}
}
}
It worked.
You have to ask for Bluetooth permissions at run time in Android 6.0 -
ActivityCompat.requestPermissions(..)
When the app first starts it immediately crashes on startup. I have narrowed down the problem to one line but not really sure what to do =/ here are the two classes in the app so far:
package com.example.bluetooth_app;
import java.util.Set;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
public class Bluetooth {
private BluetoothAdapter mBluetoothAdapter;
private Activity activity; //Activity to store main window's activity
private ArrayAdapter<String> pDevices; //Array adapter for storing already paired devices
private ArrayAdapter<String> nDevices; //Array adapter for storing newly discovered devices
private IntentFilter filter; //Filter for catching bluetooth device actions
private Button sButton; //Scan button
private ListView lvBox; //listview box
/**
* default constructor, basic initializations
*/
public Bluetooth(Activity activity) {
this.activity = activity; //Set class activity to the activity passed to it by the main activity window
pDevices = new ArrayAdapter<String>(activity, R.layout.activity_bluetooth__app);
nDevices = new ArrayAdapter<String>(activity, R.layout.activity_bluetooth__app);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
sButton = (Button) activity.findViewById(R.id.scanButton); //sButton = scan button
sButton.setOnClickListener(new View.OnClickListener() { //Listener to check if button is pressed
public void onClick(View v) { //If button is pressed start discovering and hide button
startDiscovering();
sButton.setVisibility(4); //Make button invisible
}
});
lvBox = (ListView) activity.findViewById(R.id.deviceList); // lvBox = deviceList listview
lvBox.setAdapter(pDevices);
// Register for broadcasts when a device is discovered
filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
activity.registerReceiver(mReceiver, filter);
// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
activity.registerReceiver(mReceiver, filter);
}
/**
* Check if bluetooth is enabled, if not enable it
*/
public void getAdapter() {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, 1);
}
}
/**
* Check if device is bluetooth compatible
*/
public boolean isCompat() {
if(mBluetoothAdapter == null) {
return false; //TODO: better error handling
} else {
return true;
}
}
/**
* Close some shit so we do not eat up resources
*/
public void destroy() {
if(mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery(); //cancel discovering devices
}
activity.unregisterReceiver(mReceiver);
}
/**
* Start discovering devices with bluetooth adapter
*/
public void startDiscovering() {
if(mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();
}
public void pairedDevices() {
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
sButton.setText("found some");
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
// Add the name and address to an array adapter to show in a ListView
pDevices.add(device.getName() + "\n" + device.getAddress());
}
}
}
/**
* The BroadcastReceiver that listens for discovered devices and changes the title when
* discovery is finished
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
nDevices.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
sButton.setVisibility(0); //Make button visible again
if (nDevices.getCount() == 0) {
sButton.setText("none");
//TODO: none found do something
}
}
}
};
}
and:
package com.example.bluetooth_app;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import com.example.bluetooth_app.Bluetooth;
public class Bluetooth_App extends ActionBarActivity {
private Bluetooth bT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth__app);
bT = new Bluetooth(this);
bT.isCompat();
bT.getAdapter();
bT.pairedDevices();
}
#Override
protected void onDestroy() {
super.onDestroy();
bT.destroy();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.bluetooth__app, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
The problem is in the first class, this line: lvBox.setAdapter(pDevices);,if I comment it out it runs just fine, if not it crashes on startup. Any help would be appreciated - thanks.
EDIT1 - no it has a listview called deviceList, XML file:
<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="com.example.bluetooth_app.Bluetooth_App" >
<Button
android:id="#+id/scanButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="28dp"
android:layout_marginTop="21dp"
android:text="Scan" />
<ListView
android:id="#+id/deviceList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignRight="#+id/button1"
android:layout_below="#+id/button1"
android:layout_marginRight="26dp"
android:layout_marginTop="48dp" >
</ListView>
</RelativeLayout>
Note that you seem to be reusing your activity's layout file R.layout.activity_bluetooth__app when initializing your ArrayAdapters in Bluetooth_App. This is probably not what you meant to do. The resource passed to the ArrayAdapter constructors should represent the layout of a single AdapterView row, and must contain a TextView with id text1 (to use a more customized row layout, you would need to subclass ArrayAdapter and override getView).
So, a couple of days ago I started working on my very first app. It's an app with a navigation drawer where users can set some website shortcuts to be displayed in the navigation drawer. If a user clicks on a website it opens in a webview. Really nothing fancy, but it's a concept I came up with to train my Android knowledge.
I finished the basics of this app this morning with some help, but now I want to add a large profile picture to the navigation drawer. (You can see it here on the left: http://www.droid-life.com/wp-content/uploads/2013/12/new-foursquare.jpg)
I understand that I should add an <ImageView> to the navigation drawer, but every time I try that the log keeps saying I have junk in my XML...
Below you can find the code I already wrote. I hope you guys can help me...
Main.java
package test.webviewapp;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class Main
extends ActionBarActivity
implements NavigationDrawerFragment.NavigationDrawerCallbacks
{
/**
* Fragment managing the behaviors, interactions and presentation of the
* navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in
* {#link #restoreActionBar()}.
*/
private CharSequence mTitle;
// we need a class level references to some objects to be able to modify the
// target address outside of onCreate()
private WebView myWebView;
private ActionBar actionBar;
private ProgressDialog progressDialog;
// keep the pair of String arrays of site names and addresses
private String[] siteNames;
private String[] siteAddresses;
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// grab the needed website arrays
siteNames = getResources().getStringArray(R.array.site_names);
siteAddresses = getResources().getStringArray(R.array.site_addresses);
// set up WebView. initial page load comes from NavDrawerFragment attach
myWebView = (WebView) findViewById(R.id.main_webview);
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.setWebChromeClient(new WebChromeClient());
myWebView.setWebViewClient(new WebViewClient()
{
#Override
public void onPageFinished(WebView view, String url)
{
// when a page has finished loading dismiss any progress dialog
if (progressDialog != null && progressDialog.isShowing())
{
progressDialog.dismiss();
}
String javascript="javascript:"+
"document.getElementById('menu-toggle').css('display','none');";
myWebView.evaluateJavascript(javascript, null);
}
});
// Set up the drawer.
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int siteIndex)
{
// user selected page load
Log.d(TAG, "(onNavSelect) received index: " + siteIndex);
loadWebPage(siteIndex);
}
public void onSectionAttached(int siteIndex)
{
// initial page load. not user selected.
loadWebPage(siteIndex);
}
public void restoreActionBar()
{
actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setTitle(mTitle);
}
private void loadWebPage(int siteIndex)
{
// lets show a progress indicator instead of a blank screen
if (progressDialog == null)
{
initProgressDialog();
}
progressDialog.show();
// load the page
Log.d(TAG, "(loadWebPage) Loading page: " + siteNames[siteIndex] + "("
+ siteAddresses[siteIndex] + ")");
mTitle = siteNames[siteIndex];
if (actionBar == null)
{
restoreActionBar();
}
else
{
actionBar.setTitle(mTitle);
}
myWebView.getSettings().setJavaScriptEnabled(true);
myWebView.loadUrl(siteAddresses[siteIndex]);
myWebView.loadUrl(siteAddresses[siteIndex]);
// progressDialog gets dismissed above in WebViewclient declaration
}
private void initProgressDialog()
{
progressDialog = new ProgressDialog(this, ProgressDialog.STYLE_SPINNER);
progressDialog.setIndeterminate(true);
progressDialog.setMessage(getString(R.string.page_load_progress_message));
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
if (!mNavigationDrawerFragment.isDrawerOpen())
{
// Only show items in the action bar relevant to this screen
// if the drawer is not showing. Otherwise, let the drawer
// decide what to show in the action bar.
getMenuInflater().inflate(R.menu.global, menu);
restoreActionBar();
return true;
}
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
if (actionBar == null)
{
restoreActionBar();
}
else
{
}
int id = item.getItemId();
if (id == R.id.action_settings)
{
myWebView.loadUrl(settingsview);
myWebView.loadUrl(settingsview);
actionBar.setTitle("Settings");
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment
extends Fragment
{
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SELECTED_SITE_INDEX = "selected_site_index";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int siteIndex)
{
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SELECTED_SITE_INDEX, siteIndex);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment()
{}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
// Here is where you can define the default page you want loaded
// or if you want to save/restore the last page viewed etc.
((Main) activity)
.onSectionAttached(getArguments().getInt(ARG_SELECTED_SITE_INDEX));
}
}
}
NavigationDrawerFragment.java
package test.webviewapp;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
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.ArrayAdapter;
import android.widget.ListView;
/**
* Fragment used for managing interactions for and presentation of a
* navigation drawer. See the <a href=
* "https://developer.android.com/design/patterns/navigation-drawer.html#Interaction"
* > design guidelines</a> for a complete explanation of the behaviors
* implemented here.
*/
public class NavigationDrawerFragment
extends Fragment
implements AdapterView.OnItemClickListener
{
private String[] siteNames;
private static final String TAG = "NavDrawerFrag";
/**
* Remember the position of the selected item.
*/
private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
/**
* Per the design guidelines, you should show the drawer on launch until
* the user manually expands it. This shared preference tracks this.
*/
private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
/**
* A pointer to the current callbacks instance (the Activity).
*/
private NavigationDrawerCallbacks mCallbacks;
/**
* Helper component that ties the action bar to the navigation drawer.
*/
private ActionBarDrawerToggle mDrawerToggle;
private DrawerLayout mDrawerLayout;
private ListView mDrawerListView;
private View mFragmentContainerView;
private int mCurrentSelectedPosition = 0;
private boolean mFromSavedInstanceState;
private boolean mUserLearnedDrawer;
public NavigationDrawerFragment()
{}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Read in the flag indicating whether or not the user has demonstrated awareness of the
// drawer. See PREF_USER_LEARNED_DRAWER for details.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
if (savedInstanceState != null)
{
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
mFromSavedInstanceState = true;
}
// Select either the default item (0) or the last selected item.
// too early to select an item
//selectItem(mCurrentSelectedPosition);
}
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
// Indicate that this fragment would like to influence the set of actions in the action bar.
setHasOptionsMenu(true);
selectItem(mCurrentSelectedPosition);
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
siteNames = getActivity().getResources().getStringArray(R.array.site_names);
Log.d(TAG, "number of sites loaded: " + siteNames.length);
mDrawerListView = (ListView) inflater.inflate(R.layout.fragment_navigation_drawer,
container,
false);
// neater to implement OnItemClickListener and define onClick() later
mDrawerListView.setOnItemClickListener(this);
String[] siteNames = getActivity().getResources().getStringArray(R.array.site_names);
mDrawerListView.setAdapter(new ArrayAdapter<String>(getActionBar().getThemedContext(),
android.R.layout.simple_list_item_1, android.R.id.text1, siteNames));
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
return mDrawerListView;
}
public boolean isDrawerOpen()
{
return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
}
/**
* Users of this fragment must call this method to set up the navigation
* drawer interactions.
*
* #param fragmentId
* The android:id of this fragment in its activity's layout.
* #param drawerLayout
* The DrawerLayout containing this fragment's UI.
*/
public void setUp(int fragmentId, DrawerLayout drawerLayout)
{
mFragmentContainerView = getActivity().findViewById(fragmentId);
mDrawerLayout = drawerLayout;
// set a custom shadow that overlays the main content when the drawer opens
//mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
// set up the drawer's list view with items and click listener
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the the proper interactions
// between the navigation drawer and the action bar app icon.
mDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout,
R.drawable.ic_drawer, R.string.navigation_drawer_open,
R.string.navigation_drawer_close)
{
#Override
public void onDrawerClosed(View drawerView)
{
super.onDrawerClosed(drawerView);
if (!isAdded())
{
return;
}
getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
}
#Override
public void onDrawerOpened(View drawerView)
{
super.onDrawerOpened(drawerView);
if (!isAdded())
{
return;
}
if (!mUserLearnedDrawer)
{
// The user manually opened the drawer; store this flag to prevent auto-showing
// the navigation drawer automatically in the future.
mUserLearnedDrawer = true;
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(getActivity());
sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
}
getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu()
}
};
// If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer,
// per the navigation drawer design guidelines.
if (!mUserLearnedDrawer && !mFromSavedInstanceState)
{
mDrawerLayout.openDrawer(mFragmentContainerView);
}
// Defer code dependent on restoration of previous instance state.
mDrawerLayout.post(new Runnable()
{
#Override
public void run()
{
mDrawerToggle.syncState();
}
});
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
private void selectItem(int siteIndex)
{
mCurrentSelectedPosition = siteIndex;
if (mDrawerListView != null)
{
Log.d(TAG, "(select) list ok");
mDrawerListView.setItemChecked(siteIndex, true);
}
if (mDrawerLayout != null)
{
Log.d(TAG, "(select) drawer ok");
mDrawerLayout.closeDrawer(mFragmentContainerView);
}
if (mCallbacks != null)
{
Log.d(TAG, "(select) callback...");
mCallbacks.onNavigationDrawerItemSelected(siteIndex);
}
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
mCallbacks = (NavigationDrawerCallbacks) activity;
}
catch (ClassCastException e)
{
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
}
}
#Override
public void onDetach()
{
super.onDetach();
mCallbacks = null;
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
}
#Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
// Forward the new configuration the drawer toggle component.
mDrawerToggle.onConfigurationChanged(newConfig);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
{
// If the drawer is open, show the global app actions in the action bar. See also
// showGlobalContextActionBar, which controls the top-left area of the action bar.
if (mDrawerLayout != null && isDrawerOpen())
{
inflater.inflate(R.menu.global, menu);
showGlobalContextActionBar();
}
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (mDrawerToggle.onOptionsItemSelected(item))
{
//fragment = new PlanetFragment();
}
return super.onOptionsItemSelected(item);
}
/**
* Per the navigation drawer design guidelines, updates the action bar to
* show the global app 'context', rather than just what's in the current
* screen.
*/
private void showGlobalContextActionBar()
{
ActionBar actionBar = getActionBar();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setTitle(R.string.app_name);
}
private ActionBar getActionBar()
{
return ((ActionBarActivity) getActivity()).getSupportActionBar();
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
Log.i(TAG, "(Click) index: " + position);
Log.i(TAG, "(Click) site: " + siteNames[position]);
selectItem(position);
}
/**
* Callbacks interface that all activities using this fragment must
* implement.
*/
public static interface NavigationDrawerCallbacks
{
/**
* Called when an item in the navigation drawer is selected.
*/
void onNavigationDrawerItemSelected(int position);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<fragment
android:id="#+id/navigation_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:name="test.webdrawerapp.NavigationDrawerFragment"
tools:layout="#layout/fragment_navigation_drawer"/>
</android.support.v4.widget.DrawerLayout>
fragment_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"
tools:context=".main$PlaceholderFragment">
</RelativeLayout>
fragment_navigation_drawer.xml
<ListView 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:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#000"
tools:context=".NavigationDrawerFragment" />
From working with you on this app before I was able to make some tweaks for you.
What had to be done was to add new Views to fragment_navigation_drawer.xml and change the NavigationDrawerFragment to match up and include the changes.
Layout
<?xml version="1.0" encoding="utf-8"?>
<!-- the root view is now a LinearLayout, all other Views are children of this -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="#cccc"
android:orientation="vertical">
<!-- a separate section to go above the list -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="horizontal">
<!-- your image, you can set it later (see NavDrawerFrag) -->
<ImageView
android:id="#+id/nav_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="15dp"
android:src="#android:drawable/ic_menu_myplaces"/>
<!-- a bit of test or a title to go with it -->
<TextView
android:id="#+id/nav_text"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Default text"/>
</LinearLayout>
<!-- some divider thing -->
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:padding="20dp"
android:background="#000000"/>
<!-- your ListView is now a child View -->
<ListView
android:id="#+id/nav_listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"/>
</LinearLayout>
Fragment
// new class level members
private ImageView mDrawerImage;
private TextView mDrawerText;
// change the View inflation and extract the new child views
// also modifying the ListView to now be a child instead of the root View
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState)
{
// need site names for list
siteNames = getActivity().getResources().getStringArray(R.array.site_names);
Log.d(TAG, "number of sites loaded: " + siteNames.length);
// inflate the parent view (the entire layout)
View view = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
// now grab the separate child views from inside it
mDrawerListView = (ListView) view.findViewById(R.id.nav_listView);
mDrawerImage = (ImageView) view.findViewById(R.id.nav_image);
mDrawerText = (TextView) view.findViewById(R.id.nav_text);
// configure the Views
mDrawerText.setText("Give it a name/title");
//mDrawerImage.setImageURI(...); // set your ImageView however you want, I just gave it one in XML
mDrawerListView.setOnItemClickListener(this);
mDrawerListView.setAdapter(new ArrayAdapter<String>(getActionBar().getThemedContext(),
android.R.layout.simple_list_item_1, android.R.id.text1, siteNames));
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
// and return the inflated view up the stack
return view;
}