rewrite HC Gallery app to support phones. In summary, this change:
- moves tablet-style layouts to layout-large* directories - adds ContentActivity to host the ContentFragment when on phones - adds an OnItemSelectedListener interface to TitlesFragment, which MainActivity implements in order to receive callbacks on click events to the list item and then pass the selected item to the ContentFragment in the manner appropriate for the current configuration... Specifically, when in two-pane mode, it updates the ContentFragment directly, and when in single-pane mode, it starts the ContentActivity with intent data about the selected item, which then updates the ContentFragment - Change CameraSample.java to CameraActivity.java for name conventions - Moves all menu strings into string resources - Fixes camera sample to properly handle front-facing camera on other devices (was broken on nexus s and on g-slate) - Fixes camera sample to handle resume state after the camera has changed (for example, when switched to a different camera, it would crash on resume) - Moves various code around between classes as appropriate for the fragment handling the action. For example, move the ActionBar.TabListener implementation to the TitlesFragment (was originally impemented by the MainActivity) - Adds logic to support devices without camera and properly declare the camera in manifest as such - Maintains the state of hidden titles list across restarts Change-Id: I27a39a68dee37325c0c3607aa0b56ab6c134d026
This commit is contained in:
@@ -19,7 +19,8 @@ package com.example.android.hcgallery;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class CameraSample extends Activity {
|
||||
/** Basic shell activity that hosts CameraFragment. */
|
||||
public class CameraActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.Activity;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.Camera;
|
||||
@@ -38,12 +35,16 @@ import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class CameraFragment extends Fragment {
|
||||
|
||||
private Preview mPreview;
|
||||
Camera mCamera;
|
||||
int mNumberOfCameras;
|
||||
int mCameraCurrentlyLocked;
|
||||
int mCurrentCamera; // Camera ID currently chosen
|
||||
int mCameraCurrentlyLocked; // Camera ID that's actually acquired
|
||||
|
||||
// The first rear facing camera
|
||||
int mDefaultCameraId;
|
||||
@@ -52,20 +53,18 @@ public class CameraFragment extends Fragment {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
||||
// Create a RelativeLayout container that will hold a SurfaceView,
|
||||
// and set it as the content of our activity.
|
||||
// Create a container that will hold a SurfaceView for camera previews
|
||||
mPreview = new Preview(this.getActivity());
|
||||
|
||||
// Find the total number of cameras available
|
||||
mNumberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
// Find the ID of the default camera
|
||||
// Find the ID of the rear-facing ("default") camera
|
||||
CameraInfo cameraInfo = new CameraInfo();
|
||||
for (int i = 0; i < mNumberOfCameras; i++) {
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
|
||||
mDefaultCameraId = i;
|
||||
mCurrentCamera = mDefaultCameraId = i;
|
||||
}
|
||||
}
|
||||
setHasOptionsMenu(mNumberOfCameras > 1);
|
||||
@@ -95,9 +94,10 @@ public class CameraFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Open the default i.e. the first rear facing camera.
|
||||
mCamera = Camera.open(mDefaultCameraId);
|
||||
mCameraCurrentlyLocked = mDefaultCameraId;
|
||||
// Use mCurrentCamera to select the camera desired to safely restore
|
||||
// the fragment after the camera has been changed
|
||||
mCamera = Camera.open(mCurrentCamera);
|
||||
mCameraCurrentlyLocked = mCurrentCamera;
|
||||
mPreview.setCamera(mCamera);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class CameraFragment extends Fragment {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle item selection
|
||||
switch (item.getItemId()) {
|
||||
case R.id.switch_cam:
|
||||
case R.id.menu_switch_cam:
|
||||
// Release this camera -> mCameraCurrentlyLocked
|
||||
if (mCamera != null) {
|
||||
mCamera.stopPreview();
|
||||
@@ -139,10 +139,9 @@ public class CameraFragment extends Fragment {
|
||||
|
||||
// Acquire the next camera and request Preview to reconfigure
|
||||
// parameters.
|
||||
mCamera = Camera
|
||||
.open((mCameraCurrentlyLocked + 1) % mNumberOfCameras);
|
||||
mCameraCurrentlyLocked = (mCameraCurrentlyLocked + 1)
|
||||
% mNumberOfCameras;
|
||||
mCurrentCamera = (mCameraCurrentlyLocked + 1) % mNumberOfCameras;
|
||||
mCamera = Camera.open(mCurrentCamera);
|
||||
mCameraCurrentlyLocked = mCurrentCamera;
|
||||
mPreview.switchCamera(mCamera);
|
||||
|
||||
// Start the preview
|
||||
@@ -152,6 +151,7 @@ public class CameraFragment extends Fragment {
|
||||
Intent intent = new Intent(this.getActivity(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -175,6 +175,7 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
Size mPreviewSize;
|
||||
List<Size> mSupportedPreviewSizes;
|
||||
Camera mCamera;
|
||||
boolean mSurfaceCreated = false;
|
||||
|
||||
Preview(Context context) {
|
||||
super(context);
|
||||
@@ -194,7 +195,7 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
if (mCamera != null) {
|
||||
mSupportedPreviewSizes = mCamera.getParameters()
|
||||
.getSupportedPreviewSizes();
|
||||
requestLayout();
|
||||
if (mSurfaceCreated) requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,11 +206,6 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
} catch (IOException exception) {
|
||||
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
|
||||
}
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
requestLayout();
|
||||
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,11 +223,18 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width,
|
||||
height);
|
||||
}
|
||||
|
||||
if (mCamera != null) {
|
||||
Camera.Parameters parameters = mCamera.getParameters();
|
||||
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
|
||||
|
||||
mCamera.setParameters(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (changed && getChildCount() > 0) {
|
||||
if (getChildCount() > 0) {
|
||||
final View child = getChildAt(0);
|
||||
|
||||
final int width = r - l;
|
||||
@@ -269,6 +272,8 @@ class Preview extends ViewGroup implements SurfaceHolder.Callback {
|
||||
} catch (IOException exception) {
|
||||
Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
|
||||
}
|
||||
if (mPreviewSize == null) requestLayout();
|
||||
mSurfaceCreated = true;
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
/** This is a shell activity that hosts ContentFragment when the device screen
|
||||
* is smaller than "large".
|
||||
*/
|
||||
public class ContentActivity extends Activity {
|
||||
private int mThemeId = 0;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
// The activity theme is the only state data that the activity needs
|
||||
// to restore. All info about the content displayed is managed by the fragment
|
||||
mThemeId = extras.getInt("theme");
|
||||
} else if (savedInstanceState != null) {
|
||||
// If there's no restore state, get the theme from the intent
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
}
|
||||
|
||||
if (mThemeId != 0) {
|
||||
setTheme(mThemeId);
|
||||
}
|
||||
|
||||
setContentView(R.layout.content_activity);
|
||||
|
||||
if (extras != null) {
|
||||
// Take the info from the intent and deliver it to the fragment so it can update
|
||||
int category = extras.getInt("category");
|
||||
int position = extras.getInt("position");
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager().findFragmentById(R.id.content_frag);
|
||||
frag.updateContentAndRecycleBitmap(category, position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("theme", mThemeId);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipData.Item;
|
||||
@@ -37,6 +36,8 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -46,8 +47,16 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/** Fragment that shows the content selected from the TitlesFragment.
|
||||
* When running on a screen size smaller than "large", this fragment is hosted in
|
||||
* ContentActivity. Otherwise, it appears side by side with the TitlesFragment
|
||||
* in MainActivity. */
|
||||
public class ContentFragment extends Fragment {
|
||||
private View mContentView;
|
||||
private int mCategory = 0;
|
||||
private int mCurPosition = 0;
|
||||
private boolean mSystemUiVisible = true;
|
||||
private boolean mSoloFragment = false;
|
||||
|
||||
// The bitmap currently used by ImageView
|
||||
private Bitmap mBitmap = null;
|
||||
@@ -55,11 +64,9 @@ public class ContentFragment extends Fragment {
|
||||
// Current action mode (contextual action bar, a.k.a. CAB)
|
||||
private ActionMode mCurrentActionMode;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
}
|
||||
|
||||
/** This is where we initialize the fragment's UI and attach some
|
||||
* event listeners to UI components.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -67,65 +74,48 @@ public class ContentFragment extends Fragment {
|
||||
final ImageView imageView = (ImageView) mContentView.findViewById(R.id.image);
|
||||
mContentView.setDrawingCacheEnabled(false);
|
||||
|
||||
// Handle drag events when a list item is dragged into the view
|
||||
mContentView.setOnDragListener(new View.OnDragListener() {
|
||||
public boolean onDrag(View v, DragEvent event) {
|
||||
public boolean onDrag(View view, DragEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
mContentView.setBackgroundColor(
|
||||
view.setBackgroundColor(
|
||||
getResources().getColor(R.color.drag_active_color));
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
break;
|
||||
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
return processDragStarted(event);
|
||||
|
||||
case DragEvent.ACTION_DROP:
|
||||
mContentView.setBackgroundColor(Color.TRANSPARENT);
|
||||
view.setBackgroundColor(Color.TRANSPARENT);
|
||||
return processDrop(event, imageView);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Keep the action bar visibility in sync with the system status bar. That is, when entering
|
||||
// 'lights out mode,' hide the action bar, and when exiting this mode, show the action bar.
|
||||
|
||||
final Activity activity = getActivity();
|
||||
mContentView.setOnSystemUiVisibilityChangeListener(
|
||||
new View.OnSystemUiVisibilityChangeListener() {
|
||||
public void onSystemUiVisibilityChange(int visibility) {
|
||||
ActionBar actionBar = activity.getActionBar();
|
||||
if (actionBar != null) {
|
||||
mContentView.setSystemUiVisibility(visibility);
|
||||
if (visibility == View.STATUS_BAR_VISIBLE) {
|
||||
actionBar.show();
|
||||
} else {
|
||||
actionBar.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide the system status bar when single-clicking a photo. This is also called
|
||||
// 'lights out mode.' Activating and deactivating this mode also invokes the listener
|
||||
// defined above, which will show or hide the action bar accordingly.
|
||||
|
||||
// Show/hide the system status bar when single-clicking a photo.
|
||||
mContentView.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
if (mContentView.getSystemUiVisibility() == View.STATUS_BAR_VISIBLE) {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
|
||||
public void onClick(View view) {
|
||||
if (mCurrentActionMode != null) {
|
||||
// If we're in an action mode, don't toggle the action bar
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSystemUiVisible) {
|
||||
setSystemUiVisible(false);
|
||||
} else {
|
||||
mContentView.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
|
||||
setSystemUiVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// When long-pressing a photo, activate the action mode for selection, showing the
|
||||
// contextual action bar (CAB).
|
||||
|
||||
mContentView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
public boolean onLongClick(View view) {
|
||||
if (mCurrentActionMode != null) {
|
||||
@@ -134,7 +124,7 @@ public class ContentFragment extends Fragment {
|
||||
|
||||
mCurrentActionMode = getActivity().startActionMode(
|
||||
mContentSelectionActionModeCallback);
|
||||
mContentView.setSelected(true);
|
||||
view.setSelected(true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -142,6 +132,99 @@ public class ContentFragment extends Fragment {
|
||||
return mContentView;
|
||||
}
|
||||
|
||||
/** This is where we perform additional setup for the fragment that's either
|
||||
* not related to the fragment's layout or must be done after the layout is drawn.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
// Set member variable for whether this fragment is the only one in the activity
|
||||
Fragment listFragment = getFragmentManager().findFragmentById(R.id.titles_frag);
|
||||
mSoloFragment = listFragment == null ? true : false;
|
||||
|
||||
if (mSoloFragment) {
|
||||
// The fragment is alone, so enable up navigation
|
||||
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
// Must call in order to get callback to onOptionsItemSelected()
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
// Current position and UI visibility should survive screen rotations.
|
||||
if (savedInstanceState != null) {
|
||||
setSystemUiVisible(savedInstanceState.getBoolean("systemUiVisible"));
|
||||
if (mSoloFragment) {
|
||||
// Restoring these members is not necessary when this fragment
|
||||
// is combined with the TitlesFragment, because when the TitlesFragment
|
||||
// is restored, it selects the appropriate item and sends the event
|
||||
// to the updateContentAndRecycleBitmap() method itself
|
||||
mCategory = savedInstanceState.getInt("category");
|
||||
mCurPosition = savedInstanceState.getInt("listPosition");
|
||||
updateContentAndRecycleBitmap(mCategory, mCurPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (mSoloFragment) {
|
||||
String title = Directory.getCategory(mCategory).getEntry(mCurPosition).getName();
|
||||
ActionBar bar = getActivity().getActionBar();
|
||||
bar.setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// This callback is used only when mSoloFragment == true (see onActivityCreated above)
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
// App icon in Action Bar clicked; go up
|
||||
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // Reuse the existing instance
|
||||
startActivity(intent);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
outState.putBoolean("systemUiVisible", mSystemUiVisible);
|
||||
}
|
||||
|
||||
/** Toggle whether the system UI (status bar / system bar) is visible.
|
||||
* This also toggles the action bar visibility.
|
||||
* @param show True to show the system UI, false to hide it.
|
||||
*/
|
||||
void setSystemUiVisible(boolean show) {
|
||||
mSystemUiVisible = show;
|
||||
|
||||
Window window = getActivity().getWindow();
|
||||
WindowManager.LayoutParams winParams = window.getAttributes();
|
||||
View view = getView();
|
||||
ActionBar actionBar = getActivity().getActionBar();
|
||||
|
||||
if (show) {
|
||||
// Show status bar (remove fullscreen flag)
|
||||
window.setFlags(0, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// Show system bar
|
||||
view.setSystemUiVisibility(View.STATUS_BAR_VISIBLE);
|
||||
// Show action bar
|
||||
actionBar.show();
|
||||
} else {
|
||||
// Add fullscreen flag (hide status bar)
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
// Hide system bar
|
||||
view.setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
|
||||
// Hide action bar
|
||||
actionBar.hide();
|
||||
}
|
||||
window.setAttributes(winParams);
|
||||
}
|
||||
|
||||
boolean processDragStarted(DragEvent event) {
|
||||
// Determine whether to continue processing drag and drop based on the
|
||||
// plain text mime type.
|
||||
@@ -176,7 +259,7 @@ public class ContentFragment extends Fragment {
|
||||
updateContentAndRecycleBitmap(category, entryId);
|
||||
// Update list fragment with selected entry.
|
||||
TitlesFragment titlesFrag = (TitlesFragment)
|
||||
getFragmentManager().findFragmentById(R.id.frag_title);
|
||||
getFragmentManager().findFragmentById(R.id.titles_frag);
|
||||
titlesFrag.selectPosition(entryId);
|
||||
return true;
|
||||
}
|
||||
@@ -185,7 +268,15 @@ public class ContentFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current image visible.
|
||||
* @param category Index position of the image category
|
||||
* @param position Index position of the image
|
||||
*/
|
||||
void updateContentAndRecycleBitmap(int category, int position) {
|
||||
mCategory = category;
|
||||
mCurPosition = position;
|
||||
|
||||
if (mCurrentActionMode != null) {
|
||||
mCurrentActionMode.finish();
|
||||
}
|
||||
@@ -203,6 +294,9 @@ public class ContentFragment extends Fragment {
|
||||
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
|
||||
}
|
||||
|
||||
/** Share the currently selected photo using an AsyncTask to compress the image
|
||||
* and then invoke the appropriate share intent.
|
||||
*/
|
||||
void shareCurrentPhoto() {
|
||||
File externalCacheDir = getActivity().getExternalCacheDir();
|
||||
if (externalCacheDir == null) {
|
||||
@@ -229,6 +323,7 @@ public class ContentFragment extends Fragment {
|
||||
* Compress and write the bitmap to disk on a separate thread.
|
||||
* @return TRUE if the write was successful, FALSE otherwise.
|
||||
*/
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
try {
|
||||
FileOutputStream fo = new FileOutputStream(tempFile, false);
|
||||
@@ -250,6 +345,7 @@ public class ContentFragment extends Fragment {
|
||||
* After doInBackground completes (either successfully or in failure), we invoke an
|
||||
* intent to share the photo. This code is run on the main (UI) thread.
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result != Boolean.TRUE) {
|
||||
return;
|
||||
@@ -283,7 +379,7 @@ public class ContentFragment extends Fragment {
|
||||
|
||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.share:
|
||||
case R.id.menu_share:
|
||||
shareCurrentPhoto();
|
||||
actionMode.finish();
|
||||
return true;
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -43,92 +44,86 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
/** This is the main "launcher" activity.
|
||||
* When running on a "large" or larger screen, this activity displays both the
|
||||
* TitlesFragments and the Content Fragment. When on a smaller screen size, this
|
||||
* activity displays only the TitlesFragment. In which case, selecting a list
|
||||
* item opens the ContentActivity, holds only the ContentFragment. */
|
||||
public class MainActivity extends Activity implements TitlesFragment.OnItemSelectedListener {
|
||||
|
||||
private static final int NOTIFICATION_DEFAULT = 1;
|
||||
private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
|
||||
|
||||
private View mActionBarView;
|
||||
private Animator mCurrentTitlesAnimator;
|
||||
private String[] mToggleLabels = {"Show Titles", "Hide Titles"};
|
||||
private int mLabelIndex = 1;
|
||||
private static final int NOTIFICATION_DEFAULT = 1;
|
||||
private static final String ACTION_DIALOG = "com.example.android.hcgallery.action.DIALOG";
|
||||
private int mThemeId = -1;
|
||||
private boolean mDualFragments = false;
|
||||
private boolean mTitlesHidden = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if(savedInstanceState != null && savedInstanceState.getInt("theme", -1) != -1) {
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
this.setTheme(mThemeId);
|
||||
if(savedInstanceState != null) {
|
||||
if (savedInstanceState.getInt("theme", -1) != -1) {
|
||||
mThemeId = savedInstanceState.getInt("theme");
|
||||
this.setTheme(mThemeId);
|
||||
}
|
||||
mTitlesHidden = savedInstanceState.getBoolean("titlesHidden");
|
||||
}
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
Directory.initializeDirectory();
|
||||
|
||||
ActionBar bar = getActionBar();
|
||||
bar.setDisplayShowTitleEnabled(false);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < Directory.getCategoryCount(); i++) {
|
||||
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
|
||||
.setTabListener(this));
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
if (frag != null) mDualFragments = true;
|
||||
|
||||
if (mTitlesHidden) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.hide(getFragmentManager().findFragmentById(R.id.titles_frag)).commit();
|
||||
}
|
||||
|
||||
mActionBarView = getLayoutInflater().inflate(
|
||||
R.layout.action_bar_custom, null);
|
||||
|
||||
bar.setCustomView(mActionBarView);
|
||||
bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_USE_LOGO);
|
||||
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
bar.setDisplayShowHomeEnabled(true);
|
||||
|
||||
// If category is not saved to the savedInstanceState,
|
||||
// 0 is returned by default.
|
||||
if(savedInstanceState != null) {
|
||||
int category = savedInstanceState.getInt("category");
|
||||
bar.selectTab(bar.getTabAt(category));
|
||||
}
|
||||
}
|
||||
|
||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.frag_title);
|
||||
titleFrag.populateTitles(tab.getPosition());
|
||||
|
||||
titleFrag.selectPosition(0);
|
||||
}
|
||||
|
||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.main_menu, menu);
|
||||
// If the device doesn't support camera, remove the camera menu item
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
|
||||
menu.removeItem(R.id.menu_camera);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
// If not showing both fragments, remove the "toggle titles" menu item
|
||||
if (!mDualFragments) {
|
||||
menu.removeItem(R.id.menu_toggleTitles);
|
||||
} else {
|
||||
menu.findItem(R.id.menu_toggleTitles).setTitle(mToggleLabels[mTitlesHidden ? 0 : 1]);
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.camera:
|
||||
Intent intent = new Intent(this, CameraSample.class);
|
||||
case R.id.menu_camera:
|
||||
Intent intent = new Intent(this, CameraActivity.class);
|
||||
intent.putExtra("theme", mThemeId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
||||
case R.id.toggleTitles:
|
||||
case R.id.menu_toggleTitles:
|
||||
toggleVisibleTitles();
|
||||
return true;
|
||||
|
||||
case R.id.toggleTheme:
|
||||
case R.id.menu_toggleTheme:
|
||||
if (mThemeId == R.style.AppTheme_Dark) {
|
||||
mThemeId = R.style.AppTheme_Light;
|
||||
} else {
|
||||
@@ -137,15 +132,15 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
this.recreate();
|
||||
return true;
|
||||
|
||||
case R.id.showDialog:
|
||||
case R.id.menu_showDialog:
|
||||
showDialog("This is indeed an awesome dialog.");
|
||||
return true;
|
||||
|
||||
case R.id.showStandardNotification:
|
||||
case R.id.menu_showStandardNotification:
|
||||
showNotification(false);
|
||||
return true;
|
||||
|
||||
case R.id.showCustomNotification:
|
||||
case R.id.menu_showCustomNotification:
|
||||
showNotification(true);
|
||||
return true;
|
||||
|
||||
@@ -154,13 +149,13 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
}
|
||||
}
|
||||
|
||||
/** Respond to the "toogle titles" item in the action bar */
|
||||
public void toggleVisibleTitles() {
|
||||
// Use these for custom animations.
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
final TitlesFragment f = (TitlesFragment) fm
|
||||
.findFragmentById(R.id.frag_title);
|
||||
.findFragmentById(R.id.titles_frag);
|
||||
final View titlesView = f.getView();
|
||||
mLabelIndex = 1 - mLabelIndex;
|
||||
|
||||
// Determine if we're in portrait, and whether we're showing or hiding the titles
|
||||
// with this toggle.
|
||||
@@ -207,6 +202,8 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
mCurrentTitlesAnimator = null;
|
||||
mTitlesHidden = false;
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,6 +223,8 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
return;
|
||||
mCurrentTitlesAnimator = null;
|
||||
fm.beginTransaction().hide(f).commit();
|
||||
mTitlesHidden = true;
|
||||
invalidateOptionsMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -234,8 +233,6 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
objectAnimator.start();
|
||||
mCurrentTitlesAnimator = objectAnimator;
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
// Manually trigger onNewIntent to check for ACTION_DIALOG.
|
||||
onNewIntent(getIntent());
|
||||
}
|
||||
@@ -313,22 +310,36 @@ public class MainActivity extends Activity implements ActionBar.TabListener {
|
||||
0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
menu.getItem(1).setTitle(mToggleLabels[mLabelIndex]);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
ActionBar bar = getActionBar();
|
||||
int category = bar.getSelectedTab().getPosition();
|
||||
outState.putInt("category", category);
|
||||
outState.putInt("theme", mThemeId);
|
||||
outState.putBoolean("titlesHidden", mTitlesHidden);
|
||||
}
|
||||
|
||||
/** Implementation for TitlesFragment.OnItemSelectedListener.
|
||||
* When the TitlesFragment receives an onclick event for a list item,
|
||||
* it's passed back to this activity through this method so that we can
|
||||
* deliver it to the ContentFragment in the manner appropriate */
|
||||
public void onItemSelected(int category, int position) {
|
||||
if (!mDualFragments) {
|
||||
// If showing only the TitlesFragment, start the ContentActivity and
|
||||
// pass it the info about the selected item
|
||||
Intent intent = new Intent(this, ContentActivity.class);
|
||||
intent.putExtra("category", category);
|
||||
intent.putExtra("position", position);
|
||||
intent.putExtra("theme", mThemeId);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// If showing both fragments, directly update the ContentFragment
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
frag.updateContentAndRecycleBitmap(category, position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Dialog implementation that shows a simple dialog as a fragment */
|
||||
public static class MyDialogFragment extends DialogFragment {
|
||||
|
||||
public static MyDialogFragment newInstance(String title) {
|
||||
|
||||
@@ -16,62 +16,172 @@
|
||||
|
||||
package com.example.android.hcgallery;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.app.ListFragment;
|
||||
import android.content.ClipData;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextPaint;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.FrameLayout.LayoutParams;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.AdapterView.OnItemLongClickListener;
|
||||
|
||||
public class TitlesFragment extends ListFragment {
|
||||
/**
|
||||
* Fragment that shows the list of images
|
||||
* As an extension of ListFragment, this fragment uses a default layout
|
||||
* that includes a single ListView, which you can acquire with getListView()
|
||||
* When running on a screen size smaller than "large", this fragment appears alone
|
||||
* in MainActivity. In this case, selecting a list item opens the ContentActivity,
|
||||
* which likewise holds only the ContentFragment.
|
||||
*/
|
||||
public class TitlesFragment extends ListFragment implements ActionBar.TabListener {
|
||||
OnItemSelectedListener mListener;
|
||||
private int mCategory = 0;
|
||||
private int mCurPosition = 0;
|
||||
private boolean mDualFragments = false;
|
||||
|
||||
/** Container Activity must implement this interface and we ensure
|
||||
* that it does during the onAttach() callback
|
||||
*/
|
||||
public interface OnItemSelectedListener {
|
||||
public void onItemSelected(int category, int position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
// Check that the container activity has implemented the callback interface
|
||||
try {
|
||||
mListener = (OnItemSelectedListener) activity;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(activity.toString()
|
||||
+ " must implement OnItemSelectedListener");
|
||||
}
|
||||
}
|
||||
|
||||
/** This is where we perform setup for the fragment that's either
|
||||
* not related to the fragment's layout or must be done after the layout is drawn.
|
||||
* Notice that this fragment does not implement onCreateView(), because it extends
|
||||
* ListFragment, which includes a ListView as the root view by default, so there's
|
||||
* no need to set up the layout.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.content_frag);
|
||||
if (frag != null) mDualFragments = true;
|
||||
|
||||
ActionBar bar = getActivity().getActionBar();
|
||||
bar.setDisplayHomeAsUpEnabled(false);
|
||||
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
|
||||
|
||||
// Must call in order to get callback to onCreateOptionsMenu()
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
Directory.initializeDirectory();
|
||||
for (int i = 0; i < Directory.getCategoryCount(); i++) {
|
||||
bar.addTab(bar.newTab().setText(Directory.getCategory(i).getName())
|
||||
.setTabListener(this));
|
||||
}
|
||||
|
||||
//Current position should survive screen rotations.
|
||||
if (savedInstanceState != null) {
|
||||
mCategory = savedInstanceState.getInt("category");
|
||||
mCurPosition = savedInstanceState.getInt("listPosition");
|
||||
bar.selectTab(bar.getTabAt(mCategory));
|
||||
}
|
||||
|
||||
populateTitles(mCategory);
|
||||
ListView lv = getListView();
|
||||
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
lv.setCacheColorHint(Color.TRANSPARENT);
|
||||
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
|
||||
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
|
||||
final String title = (String) ((TextView) v).getText();
|
||||
lv.setCacheColorHint(Color.TRANSPARENT); // Improves scrolling performance
|
||||
|
||||
// Set up clip data with the category||entry_id format.
|
||||
final String textData = String.format("%d||%d", mCategory, pos);
|
||||
ClipData data = ClipData.newPlainText(title, textData);
|
||||
v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (mDualFragments) {
|
||||
// Highlight the currently selected item
|
||||
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
// Enable drag and dropping
|
||||
lv.setOnItemLongClickListener(new OnItemLongClickListener() {
|
||||
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
|
||||
final String title = (String) ((TextView) v).getText();
|
||||
|
||||
selectPosition(mCurPosition);
|
||||
// Set up clip data with the category||entry_id format.
|
||||
final String textData = String.format("%d||%d", mCategory, pos);
|
||||
ClipData data = ClipData.newPlainText(title, textData);
|
||||
v.startDrag(data, new MyDragShadowBuilder(v), null, 0);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If showing both fragments, select the appropriate list item by default
|
||||
if (mDualFragments) selectPosition(mCurPosition);
|
||||
|
||||
// Attach a GlobalLayoutListener so that we get a callback when the layout
|
||||
// has finished drawing. This is necessary so that we can apply top-margin
|
||||
// to the ListView in order to dodge the ActionBar. Ordinarily, that's not
|
||||
// necessary, but we've set the ActionBar to "overlay" mode using our theme,
|
||||
// so the layout does not account for the action bar position on its own.
|
||||
ViewTreeObserver observer = getListView().getViewTreeObserver();
|
||||
observer.addOnGlobalLayoutListener(layoutListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
// Always detach ViewTreeObserver listeners when the view tears down
|
||||
getListView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener);
|
||||
}
|
||||
|
||||
/** Attaches an adapter to the fragment's ListView to populate it with items */
|
||||
public void populateTitles(int category) {
|
||||
DirectoryCategory cat = Directory.getCategory(category);
|
||||
String[] items = new String[cat.getEntryCount()];
|
||||
for (int i = 0; i < cat.getEntryCount(); i++)
|
||||
items[i] = cat.getEntry(i).getName();
|
||||
// Convenience method to attach an adapter to ListFragment's ListView
|
||||
setListAdapter(new ArrayAdapter<String>(getActivity(),
|
||||
R.layout.title_list_item, items));
|
||||
mCategory = category;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
// Send the event to the host activity via OnItemSelectedListener callback
|
||||
mListener.onItemSelected(mCategory, position);
|
||||
mCurPosition = position;
|
||||
}
|
||||
|
||||
/** Called to select an item from the listview */
|
||||
public void selectPosition(int position) {
|
||||
// Only if we're showing both fragments should the item be "highlighted"
|
||||
if (mDualFragments) {
|
||||
ListView lv = getListView();
|
||||
lv.setItemChecked(position, true);
|
||||
}
|
||||
// Calls the parent activity's implementation of the OnItemSelectedListener
|
||||
// so the activity can pass the event to the sibling fragment as appropriate
|
||||
mListener.onItemSelected(mCategory, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
}
|
||||
|
||||
/** This defines how the draggable list items appear during a drag event */
|
||||
private class MyDragShadowBuilder extends View.DragShadowBuilder {
|
||||
private Drawable mShadow;
|
||||
|
||||
@@ -93,38 +203,53 @@ public class TitlesFragment extends ListFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public void populateTitles(int category) {
|
||||
DirectoryCategory cat = Directory.getCategory(category);
|
||||
String[] items = new String[cat.getEntryCount()];
|
||||
for (int i = 0; i < cat.getEntryCount(); i++)
|
||||
items[i] = cat.getEntry(i).getName();
|
||||
setListAdapter(new ArrayAdapter<String>(getActivity(),
|
||||
R.layout.title_list_item, items));
|
||||
mCategory = category;
|
||||
// Because the fragment doesn't have a reliable callback to notify us when
|
||||
// the activity's layout is completely drawn, this OnGlobalLayoutListener provides
|
||||
// the necessary callback so we can add top-margin to the ListView in order to dodge
|
||||
// the ActionBar. Which is necessary because the ActionBar is in overlay mode, meaning
|
||||
// that it will ordinarily sit on top of the activity layout as a top layer and
|
||||
// the ActionBar height can vary. Specifically, when on a small/normal size screen,
|
||||
// the action bar tabs appear in a second row, making the action bar twice as tall.
|
||||
ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
int barHeight = getActivity().getActionBar().getHeight();
|
||||
ListView listView = getListView();
|
||||
FrameLayout.LayoutParams params = (LayoutParams) listView.getLayoutParams();
|
||||
// The list view top-margin should always match the action bar height
|
||||
if (params.topMargin != barHeight) {
|
||||
params.topMargin = barHeight;
|
||||
listView.setLayoutParams(params);
|
||||
}
|
||||
// The action bar doesn't update its height when hidden, so make top-margin zero
|
||||
if (!getActivity().getActionBar().isShowing()) {
|
||||
params.topMargin = 0;
|
||||
listView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* The following are callbacks implemented for the ActionBar.TabListener,
|
||||
* which this fragment implements to handle events when tabs are selected.
|
||||
*/
|
||||
|
||||
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
TitlesFragment titleFrag = (TitlesFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.titles_frag);
|
||||
titleFrag.populateTitles(tab.getPosition());
|
||||
|
||||
if (mDualFragments) {
|
||||
titleFrag.selectPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||
updateImage(position);
|
||||
/* These must be implemented, but we don't use them */
|
||||
|
||||
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
private void updateImage(int position) {
|
||||
ContentFragment frag = (ContentFragment) getFragmentManager()
|
||||
.findFragmentById(R.id.frag_content);
|
||||
frag.updateContentAndRecycleBitmap(mCategory, position);
|
||||
mCurPosition = position;
|
||||
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
|
||||
}
|
||||
|
||||
public void selectPosition(int position) {
|
||||
ListView lv = getListView();
|
||||
lv.setItemChecked(position, true);
|
||||
updateImage(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("listPosition", mCurPosition);
|
||||
outState.putInt("category", mCategory);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user