diff --git a/samples/browseable/AgendaData/Wearable/AndroidManifest.xml b/samples/browseable/AgendaData/Wearable/AndroidManifest.xml index dcab6227a..e6dbab705 100644 --- a/samples/browseable/AgendaData/Wearable/AndroidManifest.xml +++ b/samples/browseable/AgendaData/Wearable/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.agendadata" > + android:targetSdkVersion="22" /> diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml index 56c9133ab..b68398974 100644 --- a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml +++ b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_app_restriction_enforcer.xml @@ -120,6 +120,7 @@ @@ -147,6 +148,7 @@ diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java index e30a9a469..361c4ac33 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/AppRestrictionEnforcerFragment.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.RestrictionEntry; import android.content.RestrictionsManager; import android.content.SharedPreferences; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; @@ -105,6 +106,8 @@ public class AppRestrictionEnforcerFragment extends Fragment implements private static final String DELIMETER = ","; private static final String SEPARATOR = ":"; + private static final boolean BUNDLE_SUPPORTED = Build.VERSION.SDK_INT >= 23; + /** * Current status of the restrictions. */ @@ -138,6 +141,15 @@ public class AppRestrictionEnforcerFragment extends Fragment implements mEditProfileAge = (EditText) view.findViewById(R.id.profile_age); mLayoutItems = (LinearLayout) view.findViewById(R.id.items); view.findViewById(R.id.item_add).setOnClickListener(this); + View bundleLayout = view.findViewById(R.id.bundle_layout); + View bundleArrayLayout = view.findViewById(R.id.bundle_array_layout); + if (BUNDLE_SUPPORTED) { + bundleLayout.setVisibility(View.VISIBLE); + bundleArrayLayout.setVisibility(View.VISIBLE); + } else { + bundleLayout.setVisibility(View.GONE); + bundleArrayLayout.setVisibility(View.GONE); + } } @Override @@ -280,7 +292,7 @@ public class AppRestrictionEnforcerFragment extends Fragment implements TextUtils.join(DELIMETER, restriction.getAllSelectedStrings())), DELIMETER)); - } else if (RESTRICTION_KEY_PROFILE.equals(key)) { + } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_PROFILE.equals(key)) { String name = null; int age = 0; for (RestrictionEntry entry : restriction.getRestrictions()) { @@ -294,7 +306,7 @@ public class AppRestrictionEnforcerFragment extends Fragment implements name = prefs.getString(RESTRICTION_KEY_PROFILE_NAME, name); age = prefs.getInt(RESTRICTION_KEY_PROFILE_AGE, age); updateProfile(name, age); - } else if (RESTRICTION_KEY_ITEMS.equals(key)) { + } else if (BUNDLE_SUPPORTED && RESTRICTION_KEY_ITEMS.equals(key)) { String itemsString = prefs.getString(RESTRICTION_KEY_ITEMS, ""); HashMap items = new HashMap<>(); for (String itemString : TextUtils.split(itemsString, DELIMETER)) { @@ -351,6 +363,9 @@ public class AppRestrictionEnforcerFragment extends Fragment implements } private void updateProfile(String name, int age) { + if (!BUNDLE_SUPPORTED) { + return; + } Bundle profile = new Bundle(); profile.putString(RESTRICTION_KEY_PROFILE_NAME, name); profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age); @@ -364,6 +379,9 @@ public class AppRestrictionEnforcerFragment extends Fragment implements } private void updateItems(Context context, Map items) { + if (!BUNDLE_SUPPORTED) { + return; + } mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items)); LayoutInflater inflater = LayoutInflater.from(context); mLayoutItems.removeAllViews(); @@ -500,6 +518,9 @@ public class AppRestrictionEnforcerFragment extends Fragment implements * @param age The value to be set for the "age" field. */ private void saveProfile(Activity activity, String name, int age) { + if (!BUNDLE_SUPPORTED) { + return; + } Bundle profile = new Bundle(); profile.putString(RESTRICTION_KEY_PROFILE_NAME, name); profile.putInt(RESTRICTION_KEY_PROFILE_AGE, age); @@ -515,6 +536,9 @@ public class AppRestrictionEnforcerFragment extends Fragment implements * @param items The values. */ private void saveItems(Activity activity, Map items) { + if (!BUNDLE_SUPPORTED) { + return; + } mCurrentRestrictions.putParcelableArray(RESTRICTION_KEY_ITEMS, convertToBundles(items)); saveRestrictions(activity); StringBuilder builder = new StringBuilder(); diff --git a/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml b/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml index 85708691b..02d83e615 100644 --- a/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml +++ b/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml @@ -59,7 +59,7 @@ limitations under the License. android:textAppearance="?android:attr/textAppearanceMedium" tools:text="@string/your_rank"/> - + - + = 23; + // Message to show when the button is clicked (String restriction) private String mMessage; @@ -82,9 +85,22 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli mTextNumber = (TextView) view.findViewById(R.id.your_number); mTextRank = (TextView) view.findViewById(R.id.your_rank); mTextApprovals = (TextView) view.findViewById(R.id.approvals_you_have); + View bundleSeparator = view.findViewById(R.id.bundle_separator); mTextProfile = (TextView) view.findViewById(R.id.your_profile); + View bundleArraySeparator = view.findViewById(R.id.bundle_array_separator); mTextItems = (TextView) view.findViewById(R.id.your_items); mButtonSayHello.setOnClickListener(this); + if (BUNDLE_SUPPORTED) { + bundleSeparator.setVisibility(View.VISIBLE); + mTextProfile.setVisibility(View.VISIBLE); + bundleArraySeparator.setVisibility(View.VISIBLE); + mTextItems.setVisibility(View.VISIBLE); + } else { + bundleSeparator.setVisibility(View.GONE); + mTextProfile.setVisibility(View.GONE); + bundleArraySeparator.setVisibility(View.GONE); + mTextItems.setVisibility(View.GONE); + } } @Override @@ -178,6 +194,9 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli } private void updateProfile(RestrictionEntry entry, Bundle restrictions) { + if (!BUNDLE_SUPPORTED) { + return; + } String name = null; int age = 0; if (restrictions == null || !restrictions.containsKey(KEY_PROFILE)) { @@ -201,6 +220,9 @@ public class AppRestrictionSchemaFragment extends Fragment implements View.OnCli } private void updateItems(RestrictionEntry entry, Bundle restrictions) { + if (!BUNDLE_SUPPORTED) { + return; + } StringBuilder builder = new StringBuilder(); if (restrictions != null) { Parcelable[] parcelables = restrictions.getParcelableArray(KEY_ITEMS); diff --git a/samples/browseable/AsymmetricFingerprintDialog/_index.jd b/samples/browseable/AsymmetricFingerprintDialog/_index.jd index 858ebaa9b..4e5987e7e 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/_index.jd +++ b/samples/browseable/AsymmetricFingerprintDialog/_index.jd @@ -1,5 +1,5 @@ -page.tags="Asymmetric Fingerprint Dialog Sample" +page.tags="AsymmetricFingerprintDialog" sample.group=Security @jd:body diff --git a/samples/browseable/AsymmetricFingerprintDialog/res/values/base-strings.xml b/samples/browseable/AsymmetricFingerprintDialog/res/values/base-strings.xml index b40ebdbf5..72dabc1d2 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/res/values/base-strings.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/res/values/base-strings.xml @@ -16,7 +16,7 @@ --> - Asymmetric Fingerprint Dialog Sample + AsymmetricFingerprintDialog Security -> Fingerprint' to set up a fingerprint", - Toast.LENGTH_LONG).show(); - purchaseButton.setEnabled(false); - return; - } - if (!mFingerprintManager.hasEnrolledFingerprints()) { - purchaseButton.setEnabled(false); - // This happens when no fingerprints are registered. - Toast.makeText(this, - "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint", - Toast.LENGTH_LONG).show(); - return; - } - createKeyPair(); - purchaseButton.setEnabled(true); - purchaseButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - findViewById(R.id.confirmation_message).setVisibility(View.GONE); - findViewById(R.id.encrypted_message).setVisibility(View.GONE); - - // Set up the crypto object for later. The object will be authenticated by use - // of the fingerprint. - if (initSignature()) { - - // Show the fingerprint dialog. The user has the option to use the fingerprint with - // crypto, or you can fall back to using a server-side verified password. - mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mSignature)); - boolean useFingerprintPreference = mSharedPreferences - .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), - true); - if (useFingerprintPreference) { - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT); - } else { - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.PASSWORD); - } - mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); - } else { - // This happens if the lock screen has been disabled or or a fingerprint got - // enrolled. Thus show the dialog to authenticate with their password first - // and ask the user if they want to authenticate with fingerprints in the - // future - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED); - mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); - } - } - }); + setContentView(R.layout.activity_main); + Button purchaseButton = (Button) findViewById(R.id.purchase_button); + if (!mKeyguardManager.isKeyguardSecure()) { + // Show a message that the user hasn't set up a fingerprint or lock screen. + Toast.makeText(this, + "Secure lock screen hasn't set up.\n" + + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", + Toast.LENGTH_LONG).show(); + purchaseButton.setEnabled(false); + return; } + //noinspection ResourceType + if (!mFingerprintManager.hasEnrolledFingerprints()) { + purchaseButton.setEnabled(false); + // This happens when no fingerprints are registered. + Toast.makeText(this, + "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint", + Toast.LENGTH_LONG).show(); + return; + } + createKeyPair(); + purchaseButton.setEnabled(true); + purchaseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + findViewById(R.id.confirmation_message).setVisibility(View.GONE); + findViewById(R.id.encrypted_message).setVisibility(View.GONE); + + // Set up the crypto object for later. The object will be authenticated by use + // of the fingerprint. + if (initSignature()) { + + // Show the fingerprint dialog. The user has the option to use the fingerprint with + // crypto, or you can fall back to using a server-side verified password. + mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mSignature)); + boolean useFingerprintPreference = mSharedPreferences + .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), + true); + if (useFingerprintPreference) { + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT); + } else { + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.PASSWORD); + } + mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } else { + // This happens if the lock screen has been disabled or or a fingerprint got + // enrolled. Thus show the dialog to authenticate with their password first + // and ask the user if they want to authenticate with fingerprints in the + // future + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED); + mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } + } + }); } /** diff --git a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java index 12873e847..e6244bfb6 100644 --- a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java @@ -156,7 +156,7 @@ public class BasicAndroidKeyStoreFragment extends Fragment { // generated. Calendar start = new GregorianCalendar(); Calendar end = new GregorianCalendar(); - end.add(1, Calendar.YEAR); + end.add(Calendar.YEAR, 1); //END_INCLUDE(create_valid_dates) @@ -316,8 +316,7 @@ public class BasicAndroidKeyStoreFragment extends Fragment { // Verify the data. s.initVerify(((KeyStore.PrivateKeyEntry) entry).getCertificate()); s.update(data); - boolean valid = s.verify(signature); - return valid; + return s.verify(signature); // END_INCLUDE(verify_data) } diff --git a/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java b/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java index 4bf853433..c2b99bc4f 100644 --- a/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java +++ b/samples/browseable/Camera2Basic/src/com.example.android.camera2basic/Camera2BasicFragment.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; @@ -116,6 +117,16 @@ public class Camera2BasicFragment extends Fragment */ private static final int STATE_PICTURE_TAKEN = 4; + /** + * Max preview width that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_WIDTH = 1920; + + /** + * Max preview height that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_HEIGHT = 1080; + /** * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a * {@link TextureView}. @@ -344,31 +355,48 @@ public class Camera2BasicFragment extends Fragment } /** - * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose - * width and height are at least as large as the respective requested values, and whose aspect - * ratio matches with the specified value. + * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that + * is at least as large as the respective texture view size, and that is at most as large as the + * respective max size, and whose aspect ratio matches with the specified value. If such size + * doesn't exist, choose the largest one that is at most as large as the respective max size, + * and whose aspect ratio matches with the specified value. * - * @param choices The list of sizes that the camera supports for the intended output class - * @param width The minimum desired width - * @param height The minimum desired height - * @param aspectRatio The aspect ratio + * @param choices The list of sizes that the camera supports for the intended output + * class + * @param textureViewWidth The width of the texture view relative to sensor coordinate + * @param textureViewHeight The height of the texture view relative to sensor coordinate + * @param maxWidth The maximum width that can be chosen + * @param maxHeight The maximum height that can be chosen + * @param aspectRatio The aspect ratio * @return The optimal {@code Size}, or an arbitrary one if none were big enough */ - private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) { + private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, + int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { + // Collect the supported resolutions that are at least as big as the preview Surface List bigEnough = new ArrayList<>(); + // Collect the supported resolutions that are smaller than the preview Surface + List notBigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { - if (option.getHeight() == option.getWidth() * h / w && - option.getWidth() >= width && option.getHeight() >= height) { - bigEnough.add(option); + if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && + option.getHeight() == option.getWidth() * h / w) { + if (option.getWidth() >= textureViewWidth && + option.getHeight() >= textureViewHeight) { + bigEnough.add(option); + } else { + notBigEnough.add(option); + } } } - // Pick the smallest of those, assuming we found any + // Pick the smallest of those big enough. If there is no one big enough, pick the + // largest of those not big enough. if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); + } else if (notBigEnough.size() > 0) { + return Collections.max(notBigEnough, new CompareSizesByArea()); } else { Log.e(TAG, "Couldn't find any suitable preview size"); return choices[0]; @@ -478,11 +506,57 @@ public class Camera2BasicFragment extends Fragment mImageReader.setOnImageAvailableListener( mOnImageAvailableListener, mBackgroundHandler); + // Find out if we need to swap dimension to get the preview size relative to sensor + // coordinate. + int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + int sensorOrientation = + characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + boolean swappedDimensions = false; + switch (displayRotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_180: + if (sensorOrientation == 90 || sensorOrientation == 270) { + swappedDimensions = true; + } + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + if (sensorOrientation == 0 || sensorOrientation == 180) { + swappedDimensions = true; + } + break; + default: + Log.e(TAG, "Display rotation is invalid: " + displayRotation); + } + + Point displaySize = new Point(); + activity.getWindowManager().getDefaultDisplay().getSize(displaySize); + int rotatedPreviewWidth = width; + int rotatedPreviewHeight = height; + int maxPreviewWidth = displaySize.x; + int maxPreviewHeight = displaySize.y; + + if (swappedDimensions) { + rotatedPreviewWidth = height; + rotatedPreviewHeight = width; + maxPreviewWidth = displaySize.y; + maxPreviewHeight = displaySize.x; + } + + if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { + maxPreviewWidth = MAX_PREVIEW_WIDTH; + } + + if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { + maxPreviewHeight = MAX_PREVIEW_HEIGHT; + } + // Danger, W.R.! Attempting to use too large a preview size could exceed the camera // bus' bandwidth limitation, resulting in gorgeous previews but the storage of // garbage capture data. mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), - width, height, largest); + rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth, + maxPreviewHeight, largest); // We fit the aspect ratio of TextureView to the size of preview we picked. int orientation = getResources().getConfiguration().orientation; diff --git a/samples/browseable/Camera2Raw/src/com.example.android.camera2raw/Camera2RawFragment.java b/samples/browseable/Camera2Raw/src/com.example.android.camera2raw/Camera2RawFragment.java index 47cce388a..bf5efe588 100644 --- a/samples/browseable/Camera2Raw/src/com.example.android.camera2raw/Camera2RawFragment.java +++ b/samples/browseable/Camera2Raw/src/com.example.android.camera2raw/Camera2RawFragment.java @@ -27,6 +27,7 @@ import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.hardware.SensorManager; @@ -156,6 +157,16 @@ public class Camera2RawFragment extends Fragment */ private static final double ASPECT_RATIO_TOLERANCE = 0.005; + /** + * Max preview width that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_WIDTH = 1920; + + /** + * Max preview height that is guaranteed by Camera2 API + */ + private static final int MAX_PREVIEW_HEIGHT = 1080; + /** * Tag for the {@link Log}. */ @@ -1033,6 +1044,8 @@ public class Camera2RawFragment extends Fragment // Find the rotation of the device relative to the native device orientation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + Point displaySize = new Point(); + activity.getWindowManager().getDefaultDisplay().getSize(displaySize); // Find the rotation of the device relative to the camera sensor's orientation. int totalRotation = sensorToDeviceRotation(mCharacteristics, deviceRotation); @@ -1042,14 +1055,29 @@ public class Camera2RawFragment extends Fragment boolean swappedDimensions = totalRotation == 90 || totalRotation == 270; int rotatedViewWidth = viewWidth; int rotatedViewHeight = viewHeight; + int maxPreviewWidth = displaySize.x; + int maxPreviewHeight = displaySize.y; + if (swappedDimensions) { rotatedViewWidth = viewHeight; rotatedViewHeight = viewWidth; + maxPreviewWidth = displaySize.y; + maxPreviewHeight = displaySize.x; + } + + // Preview should not be larger than display size and 1080p. + if (maxPreviewWidth > MAX_PREVIEW_WIDTH) { + maxPreviewWidth = MAX_PREVIEW_WIDTH; + } + + if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) { + maxPreviewHeight = MAX_PREVIEW_HEIGHT; } // Find the best preview size for these view dimensions and configured JPEG size. Size previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class), - rotatedViewWidth, rotatedViewHeight, largestJpeg); + rotatedViewWidth, rotatedViewHeight, maxPreviewWidth, maxPreviewHeight, + largestJpeg); if (swappedDimensions) { mTextureView.setAspectRatio( @@ -1580,31 +1608,47 @@ public class Camera2RawFragment extends Fragment } /** - * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose - * width and height are at least as large as the respective requested values, and whose aspect - * ratio matches with the specified value. + * Given {@code choices} of {@code Size}s supported by a camera, choose the smallest one that + * is at least as large as the respective texture view size, and that is at most as large as the + * respective max size, and whose aspect ratio matches with the specified value. If such size + * doesn't exist, choose the largest one that is at most as large as the respective max size, + * and whose aspect ratio matches with the specified value. * - * @param choices The list of sizes that the camera supports for the intended output class - * @param width The minimum desired width - * @param height The minimum desired height - * @param aspectRatio The aspect ratio + * @param choices The list of sizes that the camera supports for the intended output + * class + * @param textureViewWidth The width of the texture view relative to sensor coordinate + * @param textureViewHeight The height of the texture view relative to sensor coordinate + * @param maxWidth The maximum width that can be chosen + * @param maxHeight The maximum height that can be chosen + * @param aspectRatio The aspect ratio * @return The optimal {@code Size}, or an arbitrary one if none were big enough */ - private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) { + private static Size chooseOptimalSize(Size[] choices, int textureViewWidth, + int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) { // Collect the supported resolutions that are at least as big as the preview Surface List bigEnough = new ArrayList<>(); + // Collect the supported resolutions that are smaller than the preview Surface + List notBigEnough = new ArrayList<>(); int w = aspectRatio.getWidth(); int h = aspectRatio.getHeight(); for (Size option : choices) { - if (option.getHeight() == option.getWidth() * h / w && - option.getWidth() >= width && option.getHeight() >= height) { - bigEnough.add(option); + if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight && + option.getHeight() == option.getWidth() * h / w) { + if (option.getWidth() >= textureViewWidth && + option.getHeight() >= textureViewHeight) { + bigEnough.add(option); + } else { + notBigEnough.add(option); + } } } - // Pick the smallest of those, assuming we found any + // Pick the smallest of those big enough. If there is no one big enough, pick the + // largest of those not big enough. if (bigEnough.size() > 0) { return Collections.min(bigEnough, new CompareSizesByArea()); + } else if (notBigEnough.size() > 0) { + return Collections.max(notBigEnough, new CompareSizesByArea()); } else { Log.e(TAG, "Couldn't find any suitable preview size"); return choices[0]; diff --git a/samples/browseable/DataLayer/Application/AndroidManifest.xml b/samples/browseable/DataLayer/Application/AndroidManifest.xml index e80846de1..ed1cec347 100644 --- a/samples/browseable/DataLayer/Application/AndroidManifest.xml +++ b/samples/browseable/DataLayer/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.datalayer" > + android:targetSdkVersion="23" /> diff --git a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java index 678e428ca..b3cb25304 100644 --- a/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java +++ b/samples/browseable/DataLayer/Wearable/src/com.example.android.wearable.datalayer/MainActivity.java @@ -23,6 +23,7 @@ import android.app.Fragment; import android.app.FragmentManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.wearable.view.DotsPageIndicator; @@ -41,6 +42,7 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; +import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.wearable.Asset; import com.google.android.gms.wearable.CapabilityApi; @@ -85,7 +87,6 @@ public class MainActivity extends Activity implements ConnectionCallbacks, private static final String CAPABILITY_2_NAME = "capability_2"; private GoogleApiClient mGoogleApiClient; - private Handler mHandler; private GridViewPager mPager; private DataFragment mDataFragment; private AssetFragment mAssetFragment; @@ -93,7 +94,6 @@ public class MainActivity extends Activity implements ConnectionCallbacks, @Override public void onCreate(Bundle b) { super.onCreate(b); - mHandler = new Handler(); setContentView(R.layout.main_activity); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setupViews(); @@ -137,15 +137,6 @@ public class MainActivity extends Activity implements ConnectionCallbacks, Log.e(TAG, "onConnectionFailed(): Failed to connect, with result: " + result); } - private void generateEvent(final String title, final String text) { - runOnUiThread(new Runnable() { - @Override - public void run() { - mDataFragment.appendItem(title, text); - } - }); - } - @Override public void onDataChanged(DataEventBuffer dataEvents) { LOGD(TAG, "onDataChanged(): " + dataEvents); @@ -155,29 +146,22 @@ public class MainActivity extends Activity implements ConnectionCallbacks, String path = event.getDataItem().getUri().getPath(); if (DataLayerListenerService.IMAGE_PATH.equals(path)) { DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem()); - Asset photo = dataMapItem.getDataMap() + Asset photoAsset = dataMapItem.getDataMap() .getAsset(DataLayerListenerService.IMAGE_KEY); - final Bitmap bitmap = loadBitmapFromAsset(mGoogleApiClient, photo); - mHandler.post(new Runnable() { - @Override - public void run() { - Log.d(TAG, "Setting background image on second page.."); - moveToPage(1); - mAssetFragment.setBackgroundImage(bitmap); - } - }); + // Loads image on background thread. + new LoadBitmapAsyncTask().execute(photoAsset); } else if (DataLayerListenerService.COUNT_PATH.equals(path)) { LOGD(TAG, "Data Changed for COUNT_PATH"); - generateEvent("DataItem Changed", event.getDataItem().toString()); + mDataFragment.appendItem("DataItem Changed", event.getDataItem().toString()); } else { LOGD(TAG, "Unrecognized path: " + path); } } else if (event.getType() == DataEvent.TYPE_DELETED) { - generateEvent("DataItem Deleted", event.getDataItem().toString()); + mDataFragment.appendItem("DataItem Deleted", event.getDataItem().toString()); } else { - generateEvent("Unknown data event type", "Type = " + event.getType()); + mDataFragment.appendItem("Unknown data event type", "Type = " + event.getType()); } } } @@ -199,20 +183,27 @@ public class MainActivity extends Activity implements ConnectionCallbacks, * Find the connected nodes that provide at least one of the given capabilities */ private void showNodes(final String... capabilityNames) { - Wearable.CapabilityApi.getAllCapabilities(mGoogleApiClient, - CapabilityApi.FILTER_REACHABLE).setResultCallback( + PendingResult pendingCapabilityResult = + Wearable.CapabilityApi.getAllCapabilities( + mGoogleApiClient, + CapabilityApi.FILTER_REACHABLE); + + pendingCapabilityResult.setResultCallback( new ResultCallback() { @Override public void onResult( CapabilityApi.GetAllCapabilitiesResult getAllCapabilitiesResult) { + if (!getAllCapabilitiesResult.getStatus().isSuccess()) { Log.e(TAG, "Failed to get capabilities"); return; } - Map - capabilitiesMap = getAllCapabilitiesResult.getAllCapabilities(); + + Map capabilitiesMap = + getAllCapabilitiesResult.getAllCapabilities(); Set nodes = new HashSet<>(); + if (capabilitiesMap.isEmpty()) { showDiscoveredNodes(nodes); return; @@ -231,7 +222,7 @@ public class MainActivity extends Activity implements ConnectionCallbacks, for (Node node : nodes) { nodesList.add(node.getDisplayName()); } - Log.d(TAG, "Connected Nodes: " + (nodesList.isEmpty() + LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty() ? "No connected device was found for the given capabilities" : TextUtils.join(",", nodesList))); String msg; @@ -246,39 +237,20 @@ public class MainActivity extends Activity implements ConnectionCallbacks, }); } - /** - * Extracts {@link android.graphics.Bitmap} data from the - * {@link com.google.android.gms.wearable.Asset} - */ - private Bitmap loadBitmapFromAsset(GoogleApiClient apiClient, Asset asset) { - if (asset == null) { - throw new IllegalArgumentException("Asset must be non-null"); - } - - InputStream assetInputStream = Wearable.DataApi.getFdForAsset( - apiClient, asset).await().getInputStream(); - - if (assetInputStream == null) { - Log.w(TAG, "Requested an unknown Asset."); - return null; - } - return BitmapFactory.decodeStream(assetInputStream); - } - @Override public void onMessageReceived(MessageEvent event) { LOGD(TAG, "onMessageReceived: " + event); - generateEvent("Message", event.toString()); + mDataFragment.appendItem("Message", event.toString()); } @Override public void onPeerConnected(Node node) { - generateEvent("Node Connected", node.getId()); + mDataFragment.appendItem("Node Connected", node.getId()); } @Override public void onPeerDisconnected(Node node) { - generateEvent("Node Disconnected", node.getId()); + mDataFragment.appendItem("Node Disconnected", node.getId()); } private void setupViews() { @@ -330,4 +302,43 @@ public class MainActivity extends Activity implements ConnectionCallbacks, } } + + /* + * Extracts {@link android.graphics.Bitmap} data from the + * {@link com.google.android.gms.wearable.Asset} + */ + private class LoadBitmapAsyncTask extends AsyncTask { + + @Override + protected Bitmap doInBackground(Asset... params) { + + if(params.length > 0) { + + Asset asset = params[0]; + + InputStream assetInputStream = Wearable.DataApi.getFdForAsset( + mGoogleApiClient, asset).await().getInputStream(); + + if (assetInputStream == null) { + Log.w(TAG, "Requested an unknown Asset."); + return null; + } + return BitmapFactory.decodeStream(assetInputStream); + + } else { + Log.e(TAG, "Asset must be non-null"); + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + + if(bitmap != null) { + LOGD(TAG, "Setting background image on second page.."); + moveToPage(1); + mAssetFragment.setBackgroundImage(bitmap); + } + } + } } diff --git a/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml b/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml index d8060a8d4..e3e6de174 100644 --- a/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml +++ b/samples/browseable/DelayedConfirmation/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.delayedconfirmation" > + android:targetSdkVersion="23" /> + android:targetSdkVersion="23" /> + android:targetSdkVersion="23" /> - Fingerprint Dialog Sample + FingerprintDialog Security -> Fingerprint' to set up a fingerprint", - Toast.LENGTH_LONG).show(); - purchaseButton.setEnabled(false); - return; - } - if (!mFingerprintManager.hasEnrolledFingerprints()) { - purchaseButton.setEnabled(false); - // This happens when no fingerprints are registered. - Toast.makeText(this, - "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint", - Toast.LENGTH_LONG).show(); - return; - } - createKey(); - purchaseButton.setEnabled(true); - purchaseButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - findViewById(R.id.confirmation_message).setVisibility(View.GONE); - findViewById(R.id.encrypted_message).setVisibility(View.GONE); - - // Set up the crypto object for later. The object will be authenticated by use - // of the fingerprint. - if (initCipher()) { - - // Show the fingerprint dialog. The user has the option to use the fingerprint with - // crypto, or you can fall back to using a server-side verified password. - mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); - boolean useFingerprintPreference = mSharedPreferences - .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), - true); - if (useFingerprintPreference) { - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT); - } else { - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.PASSWORD); - } - mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); - } else { - // This happens if the lock screen has been disabled or or a fingerprint got - // enrolled. Thus show the dialog to authenticate with their password first - // and ask the user if they want to authenticate with fingerprints in the - // future - mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); - mFragment.setStage( - FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED); - mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); - } - } - }); + setContentView(R.layout.activity_main); + Button purchaseButton = (Button) findViewById(R.id.purchase_button); + if (!mKeyguardManager.isKeyguardSecure()) { + // Show a message that the user hasn't set up a fingerprint or lock screen. + Toast.makeText(this, + "Secure lock screen hasn't set up.\n" + + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", + Toast.LENGTH_LONG).show(); + purchaseButton.setEnabled(false); + return; } + + //noinspection ResourceType + if (!mFingerprintManager.hasEnrolledFingerprints()) { + purchaseButton.setEnabled(false); + // This happens when no fingerprints are registered. + Toast.makeText(this, + "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint", + Toast.LENGTH_LONG).show(); + return; + } + createKey(); + purchaseButton.setEnabled(true); + purchaseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + findViewById(R.id.confirmation_message).setVisibility(View.GONE); + findViewById(R.id.encrypted_message).setVisibility(View.GONE); + + // Set up the crypto object for later. The object will be authenticated by use + // of the fingerprint. + if (initCipher()) { + + // Show the fingerprint dialog. The user has the option to use the fingerprint with + // crypto, or you can fall back to using a server-side verified password. + mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); + boolean useFingerprintPreference = mSharedPreferences + .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), + true); + if (useFingerprintPreference) { + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT); + } else { + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.PASSWORD); + } + mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } else { + // This happens if the lock screen has been disabled or or a fingerprint got + // enrolled. Thus show the dialog to authenticate with their password first + // and ask the user if they want to authenticate with fingerprints in the + // future + mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); + mFragment.setStage( + FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED); + mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); + } + } + }); } /** diff --git a/samples/browseable/Flashlight/AndroidManifest.xml b/samples/browseable/Flashlight/AndroidManifest.xml index 738ba9d37..1eb15d072 100644 --- a/samples/browseable/Flashlight/AndroidManifest.xml +++ b/samples/browseable/Flashlight/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.flashlight" > + android:targetSdkVersion="22" /> diff --git a/samples/browseable/Flashlight/_index.jd b/samples/browseable/Flashlight/_index.jd index 1858b3ec4..15a65fc78 100644 --- a/samples/browseable/Flashlight/_index.jd +++ b/samples/browseable/Flashlight/_index.jd @@ -6,6 +6,6 @@ sample.group=Wearable

Wearable activity that uses your wearable screen as a flashlight. There is also - a party-mode option, if you want to make things interesting. + a party-mode option (swipe left), if you want to make things interesting.

diff --git a/samples/browseable/Geofencing/Application/AndroidManifest.xml b/samples/browseable/Geofencing/Application/AndroidManifest.xml index d07a2659f..d1eabc3d7 100644 --- a/samples/browseable/Geofencing/Application/AndroidManifest.xml +++ b/samples/browseable/Geofencing/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.geofencing"> + android:targetSdkVersion="22" /> diff --git a/samples/browseable/Geofencing/Wearable/AndroidManifest.xml b/samples/browseable/Geofencing/Wearable/AndroidManifest.xml index 082f396b6..f25cc4472 100644 --- a/samples/browseable/Geofencing/Wearable/AndroidManifest.xml +++ b/samples/browseable/Geofencing/Wearable/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.geofencing" > + android:targetSdkVersion="22" /> diff --git a/samples/browseable/GridViewPager/AndroidManifest.xml b/samples/browseable/GridViewPager/AndroidManifest.xml index 5c362dcb5..e25cd6373 100644 --- a/samples/browseable/GridViewPager/AndroidManifest.xml +++ b/samples/browseable/GridViewPager/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.gridviewpager" > + android:targetSdkVersion="22" /> diff --git a/samples/browseable/JumpingJack/AndroidManifest.xml b/samples/browseable/JumpingJack/AndroidManifest.xml index 02b7a4fff..f6cf22047 100644 --- a/samples/browseable/JumpingJack/AndroidManifest.xml +++ b/samples/browseable/JumpingJack/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.jumpingjack"> + android:targetSdkVersion="22" /> diff --git a/samples/browseable/Notifications/Application/AndroidManifest.xml b/samples/browseable/Notifications/Application/AndroidManifest.xml index 3f1274d87..6a17ad8c3 100644 --- a/samples/browseable/Notifications/Application/AndroidManifest.xml +++ b/samples/browseable/Notifications/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.support.wearable.notifications" > + android:targetSdkVersion="23" /> diff --git a/samples/browseable/Notifications/Wearable/AndroidManifest.xml b/samples/browseable/Notifications/Wearable/AndroidManifest.xml index 34a29ff66..a446fd9bb 100644 --- a/samples/browseable/Notifications/Wearable/AndroidManifest.xml +++ b/samples/browseable/Notifications/Wearable/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.support.wearable.notifications" > + android:targetSdkVersion="22" /> diff --git a/samples/browseable/Quiz/Application/AndroidManifest.xml b/samples/browseable/Quiz/Application/AndroidManifest.xml index 801a47323..8fabd42de 100644 --- a/samples/browseable/Quiz/Application/AndroidManifest.xml +++ b/samples/browseable/Quiz/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.quiz" > + android:targetSdkVersion="23" /> + android:targetSdkVersion="23" /> + android:targetSdkVersion="22" /> diff --git a/samples/browseable/SpeedTracker/Application/AndroidManifest.xml b/samples/browseable/SpeedTracker/Application/AndroidManifest.xml index 44284d4fd..be88f6d68 100644 --- a/samples/browseable/SpeedTracker/Application/AndroidManifest.xml +++ b/samples/browseable/SpeedTracker/Application/AndroidManifest.xml @@ -2,25 +2,35 @@ + + + + + + - - + + + - + + - + android:theme="@style/Theme.AppCompat.Light" > diff --git a/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml b/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml index a18c64484..17a8f6a9d 100644 --- a/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml +++ b/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml @@ -21,7 +21,8 @@ + android:layout_height="wrap_content" + android:layout_marginTop="10dp">