/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.samples.apps.iosched.map; import android.Manifest; import android.app.FragmentManager; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import com.google.samples.apps.iosched.R; import com.google.samples.apps.iosched.navigation.NavigationModel; import com.google.samples.apps.iosched.provider.ScheduleContract; import com.google.samples.apps.iosched.ui.BaseActivity; import com.google.samples.apps.iosched.util.AnalyticsHelper; import com.google.samples.apps.iosched.util.PermissionsUtils; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; /** * Activity that displays a {@link com.google.samples.apps.iosched.map.MapFragment} and a {@link * com.google.samples.apps.iosched.map.MapInfoFragment}. Supports 'detached' mode, where the toolbar * contains an up navigation option that finishes this Activity. (see {@link #EXTRA_DETACHED_MODE} * Optionally a room can be specified via {@link #EXTRA_ROOM} that pans the map to its indicated * marker. * * @see com.google.samples.apps.iosched.map.MapInfoFragment#newInstace(android.content.Context) */ public class MapActivity extends BaseActivity implements SlideableInfoFragment.Callback, MapFragment.Callbacks, ActivityCompat.OnRequestPermissionsResultCallback { private static final String TAG = makeLogTag(MapActivity.class); private static final int REQUEST_LOCATION_PERMISSION = 1; public static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; private static final String SCREEN_LABEL = "Map"; /** * When specified, will automatically point the map to the requested room. */ public static final String EXTRA_ROOM = "com.google.android.iosched.extra.ROOM"; public static final String EXTRA_DETACHED_MODE = "com.google.samples.apps.iosched.EXTRA_DETACHED_MODE"; public static final String BUNDLE_STATE_MAPVIEW = "mapview"; private boolean mDetachedMode; protected MapFragment mMapFragment; private MapInfoFragment mInfoFragment; private View mInfoContainer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getFragmentManager(); mMapFragment = (MapFragment) fm.findFragmentByTag("map"); mDetachedMode = getIntent().getBooleanExtra(EXTRA_DETACHED_MODE, false); if (isFinishing()) { return; } setContentView(R.layout.map_act); mInfoContainer = findViewById(R.id.map_detail_popup); // ANALYTICS SCREEN: View the Map screen on a phone // Contains: Nothing (Page name is a constant) AnalyticsHelper.sendScreenView(SCREEN_LABEL); overridePendingTransition(0, 0); } @Override protected void onSaveInstanceState(Bundle outState) { // Save the mapview state in a separate bundle parameter final Bundle mapviewState = new Bundle(); mMapFragment.onSaveInstanceState(mapviewState); outState.putBundle(BUNDLE_STATE_MAPVIEW, mapviewState); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (mDetachedMode) { final Toolbar toolbar = getToolbar(); toolbar.setNavigationIcon(R.drawable.ic_up); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { finish(); } }); } if (mMapFragment == null) { // Either restore the state of the map or read it from the Intent extras. if (savedInstanceState != null) { // Restore state from existing bundle Bundle previousState = savedInstanceState.getBundle(BUNDLE_STATE_MAPVIEW); mMapFragment = MapFragment.newInstance(previousState); } else { // Get highlight room id (if specified in intent extras) final String highlightRoomId = getIntent().hasExtra(EXTRA_ROOM) ? getIntent() .getExtras().getString(EXTRA_ROOM) : null; mMapFragment = MapFragment.newInstance(highlightRoomId); } getFragmentManager().beginTransaction() .add(R.id.fragment_container_map, mMapFragment, "map") .commit(); } if (mInfoFragment == null) { mInfoFragment = MapInfoFragment.newInstace(this); getFragmentManager().beginTransaction() .add(R.id.fragment_container_map_info, mInfoFragment, "mapsheet") .commit(); } mDetachedMode = getIntent().getBooleanExtra(EXTRA_DETACHED_MODE, false); attemptEnableMyLocation(); } @Override public void onBackPressed() { if (mInfoFragment != null && mInfoFragment.isExpanded()) { mInfoFragment.minimize(); } else { super.onBackPressed(); } } @Override protected NavigationModel.NavigationItemEnum getSelfNavDrawerItem() { return mDetachedMode ? NavigationModel.NavigationItemEnum.INVALID : NavigationModel.NavigationItemEnum.MAP; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (getIntent().getBooleanExtra(EXTRA_DETACHED_MODE, false) && item.getItemId() == android.R.id.home) { finish(); } return super.onOptionsItemSelected(item); } /** * Adjust the padding in the map fragment when the info fragment has been resized. The size is * self reported from the fragment and has to be adjusted before it can be applied to the map. * <p/> * For {@link com.google.samples.apps.iosched.map.InlineInfoFragment} (that is only displayed on * the left of the screen), the full extend of its container layout (including padding) is * passed to the map fragment. For {@link com.google.samples.apps.iosched.map * .SlideableInfoFragment} (that is only displayed at the bottom of the screen), its actual * height is passed through to the map. */ @Override public void onInfoSizeChanged(int left, int top, int right, int bottom) { if (mMapFragment != null) { if (mInfoFragment instanceof InlineInfoFragment) { // InlineInfoFragment is shown on the left on tablet layouts. // Use the right edge of its containers for padding of the map. //TODO: RTL support - compare left and right positioning if (right > 0) { mMapFragment.setMapInsets(mInfoContainer.getRight(), 0, 0, 0); } else { mMapFragment.setMapInsets(0, 0, 0, 0); } } else if (mInfoFragment instanceof SlideableInfoFragment) { // SlideableInfoFragment is only shown on phone layouts at the bottom of the screen, // but only up to 50% of the total height of the screen if ((float) top / (float) bottom > SlideableInfoFragment.MAX_PANEL_PADDING_FACTOR) { mMapFragment.setMapInsets(0, 0, 0, bottom - top); } final int bottomPadding = Math.min(bottom - top, Math.round(bottom * SlideableInfoFragment.MAX_PANEL_PADDING_FACTOR)); mMapFragment.setMapInsets(0, 0, 0, bottomPadding); } // F } } @Override public void onSessionClicked(String sessionId) { // ANALYTICS EVENT: Click on a session in the Maps screen. // Contains: The session ID. AnalyticsHelper.sendEvent(SCREEN_LABEL, "selectsession", sessionId); startActivity(new Intent(Intent.ACTION_VIEW, ScheduleContract.Sessions.buildSessionUri(sessionId))); } @Override public void onInfoShowVenue() { if (mInfoFragment != null) { mInfoFragment.showVenue(); } setTabletInfoVisibility(View.VISIBLE); } @Override public void onInfoShowTitle(String label, int icon) { if (mInfoFragment != null) { mInfoFragment.showTitleOnly(icon, label); } setTabletInfoVisibility(View.VISIBLE); } @Override public void onInfoShowSessionlist(String roomId, String roomTitle, int roomType) { if (mInfoFragment != null) { mInfoFragment.showSessionList(roomId, roomTitle, roomType); } setTabletInfoVisibility(View.VISIBLE); } @Override public void onInfoShowFirstSessionTitle(String roomId, String roomTitle, int roomType) { if (mInfoFragment != null) { mInfoFragment.showFirstSessionTitle(roomId, roomTitle, roomType); } setTabletInfoVisibility(View.VISIBLE); } @Override public void onInfoHide() { if (mInfoFragment != null) { mInfoFragment.hide(); } setTabletInfoVisibility(View.GONE); } private void setTabletInfoVisibility(int visibility) { final View view = findViewById(R.id.map_detail_popup); if (view != null) { view.setVisibility(visibility); } } /** * Enables the 'My Location' feature on the map fragment if the location permission has been * granted. If the permission is not available yet, it is requested. */ public void attemptEnableMyLocation() { // Check if the permission has already been granted. if (PermissionsUtils.permissionsAlreadyGranted(this, PERMISSIONS)) { // Permission has been granted. if (mMapFragment != null) { mMapFragment.setMyLocationEnabled(true); return; } } // The permissions have not been granted yet. Request them. ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_LOCATION_PERMISSION); } @Override public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { if (requestCode != REQUEST_LOCATION_PERMISSION) { return; } if (permissions.length == PERMISSIONS.length && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission has been granted. if (mMapFragment != null) { mMapFragment.setMyLocationEnabled(true); } } else { // Permission was denied. Display error message that disappears after a short while. PermissionsUtils.displayConditionalPermissionDenialSnackbar(this, R.string.map_permission_denied, PERMISSIONS, REQUEST_LOCATION_PERMISSION, false); } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }