diff options
309 files changed, 4485 insertions, 2904 deletions
diff --git a/build/sdk.atree b/build/sdk.atree index 093bd3a38..c1e86e213 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -286,7 +286,7 @@ developers/build/prebuilts/gradle/TextSwitcher/ sam developers/build/prebuilts/gradle/HorizontalPaging/ samples/${PLATFORM_NAME}/ui/HorizontalPaging developers/build/prebuilts/gradle/ActionBarCompat-Styled/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-Styled developers/build/prebuilts/gradle/ActionBarCompat-ListPopupMenu/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-ListPopupMenu -developers/build/prebuilts/gradle/ActionBarCompat-ShareActionProvider/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-ShareActionProvider +developers/build/prebuilts/gradle/ShareActionProvider/ samples/${PLATFORM_NAME}/ui/ShareActionProvider developers/build/prebuilts/gradle/ActionBarCompat-Basic/ samples/${PLATFORM_NAME}/ui/ActionBarCompat-Basic developers/build/prebuilts/gradle/BasicNotifications/ samples/${PLATFORM_NAME}/ui/BasicNotifications developers/build/prebuilts/gradle/CustomNotifications/ samples/${PLATFORM_NAME}/ui/CustomNotifications diff --git a/samples/NotePad/AndroidManifest.xml b/samples/NotePad/AndroidManifest.xml index ead782925..51e848d8e 100644 --- a/samples/NotePad/AndroidManifest.xml +++ b/samples/NotePad/AndroidManifest.xml @@ -107,14 +107,6 @@ </intent-filter> </activity> - <activity android:name="NotesLiveFolder" android:label="@string/live_folder_name" - android:icon="@drawable/live_folder_notes"> - <intent-filter> - <action android:name="android.intent.action.CREATE_LIVE_FOLDER" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - </activity> - </application> </manifest> diff --git a/samples/NotePad/res/values/strings.xml b/samples/NotePad/res/values/strings.xml index 26d23d072..508fa439f 100644 --- a/samples/NotePad/res/values/strings.xml +++ b/samples/NotePad/res/values/strings.xml @@ -40,4 +40,5 @@ <string name="error_title">Error</string> <string name="error_message">Error loading note</string> <string name="nothing_to_save">There is nothing to save</string> + <string name="title_blank">Blank title not saved</string> </resources>
\ No newline at end of file diff --git a/samples/NotePad/src/com/example/android/notepad/NoteEditor.java b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java index 59d6f1290..b8b070f20 100644 --- a/samples/NotePad/src/com/example/android/notepad/NoteEditor.java +++ b/samples/NotePad/src/com/example/android/notepad/NoteEditor.java @@ -17,13 +17,16 @@ package com.example.android.notepad; import android.app.Activity; +import android.app.LoaderManager; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.content.CursorLoader; import android.content.Intent; +import android.content.Loader; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Canvas; @@ -37,19 +40,15 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.EditText; +import com.example.android.notepad.NotePad.Notes; /** * This Activity handles "editing" a note, where editing is responding to * {@link Intent#ACTION_VIEW} (request to view data), edit a note * {@link Intent#ACTION_EDIT}, create a note {@link Intent#ACTION_INSERT}, or * create a new note from the current contents of the clipboard {@link Intent#ACTION_PASTE}. - * - * NOTE: Notice that the provider operations in this Activity are taking place on the UI thread. - * This is not a good practice. It is only done here to make the code more readable. A real - * application should use the {@link android.content.AsyncQueryHandler} - * or {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread. */ -public class NoteEditor extends Activity { +public class NoteEditor extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { // For logging and debugging purposes private static final String TAG = "NoteEditor"; @@ -71,10 +70,11 @@ public class NoteEditor extends Activity { private static final int STATE_EDIT = 0; private static final int STATE_INSERT = 1; + private static final int LOADER_ID = 1; + // Global mutable variables private int mState; private Uri mUri; - private Cursor mCursor; private EditText mText; private String mOriginalContent; @@ -139,6 +139,11 @@ public class NoteEditor extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Recovering the instance state from a previously destroyed Activity instance + if (savedInstanceState != null) { + mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT); + } + /* * Creates an Intent to use when the Activity object's result is sent back to the * caller. @@ -166,6 +171,8 @@ public class NoteEditor extends Activity { // Sets the Activity state to INSERT, gets the general note URI, and inserts an // empty record in the provider mState = STATE_INSERT; + setTitle(getText(R.string.title_create)); + mUri = getContentResolver().insert(intent.getData(), null); /* @@ -197,24 +204,10 @@ public class NoteEditor extends Activity { return; } - /* - * Using the URI passed in with the triggering Intent, gets the note or notes in - * the provider. - * Note: This is being done on the UI thread. It will block the thread until the query - * completes. In a sample app, going against a simple provider based on a local database, - * the block will be momentary, but in a real app you should use - * android.content.AsyncQueryHandler or android.os.AsyncTask. - */ - mCursor = managedQuery( - mUri, // The URI that gets multiple notes from the provider. - PROJECTION, // A projection that returns the note ID and note content for each note. - null, // No "where" clause selection criteria. - null, // No "where" clause selection values. - null // Use the default sort order (modification date, descending) - ); + // Initialize the LoaderManager and start the query + getLoaderManager().initLoader(LOADER_ID, null, this); // For a paste, initializes the data from clipboard. - // (Must be done after mCursor is initialized.) if (Intent.ACTION_PASTE.equals(action)) { // Does the paste performPaste(); @@ -227,87 +220,12 @@ public class NoteEditor extends Activity { // Gets a handle to the EditText in the the layout. mText = (EditText) findViewById(R.id.note); - - /* - * If this Activity had stopped previously, its state was written the ORIGINAL_CONTENT - * location in the saved Instance state. This gets the state. - */ - if (savedInstanceState != null) { - mOriginalContent = savedInstanceState.getString(ORIGINAL_CONTENT); - } } - /** - * This method is called when the Activity is about to come to the foreground. This happens - * when the Activity comes to the top of the task stack, OR when it is first starting. - * - * Moves to the first note in the list, sets an appropriate title for the action chosen by - * the user, puts the note contents into the TextView, and saves the original text as a - * backup. - */ - @Override - protected void onResume() { - super.onResume(); - - /* - * mCursor is initialized, since onCreate() always precedes onResume for any running - * process. This tests that it's not null, since it should always contain data. - */ - if (mCursor != null) { - // Requery in case something changed while paused (such as the title) - mCursor.requery(); - - /* Moves to the first record. Always call moveToFirst() before accessing data in - * a Cursor for the first time. The semantics of using a Cursor are that when it is - * created, its internal index is pointing to a "place" immediately before the first - * record. - */ - mCursor.moveToFirst(); - - // Modifies the window title for the Activity according to the current Activity state. - if (mState == STATE_EDIT) { - // Set the title of the Activity to include the note title - int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE); - String title = mCursor.getString(colTitleIndex); - Resources res = getResources(); - String text = String.format(res.getString(R.string.title_edit), title); - setTitle(text); - // Sets the title to "create" for inserts - } else if (mState == STATE_INSERT) { - setTitle(getText(R.string.title_create)); - } - - /* - * onResume() may have been called after the Activity lost focus (was paused). - * The user was either editing or creating a note when the Activity paused. - * The Activity should re-display the text that had been retrieved previously, but - * it should not move the cursor. This helps the user to continue editing or entering. - */ - - // Gets the note text from the Cursor and puts it in the TextView, but doesn't change - // the text cursor's position. - int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE); - String note = mCursor.getString(colNoteIndex); - mText.setTextKeepState(note); - - // Stores the original note text, to allow the user to revert changes. - if (mOriginalContent == null) { - mOriginalContent = note; - } - - /* - * Something is wrong. The Cursor should always contain data. Report an error in the - * note. - */ - } else { - setTitle(getText(R.string.error_title)); - mText.setText(getText(R.string.error_message)); - } - } /** - * This method is called when an Activity loses focus during its normal operation, and is then - * later on killed. The Activity has a chance to save its state so that the system can restore + * This method is called when an Activity loses focus during its normal operation. + * The Activity has a chance to save its state so that the system can restore * it. * * Notice that this method isn't a normal part of the Activity lifecycle. It won't be called @@ -316,37 +234,52 @@ public class NoteEditor extends Activity { @Override protected void onSaveInstanceState(Bundle outState) { // Save away the original text, so we still have it if the activity - // needs to be killed while paused. + // needs to be re-created. outState.putString(ORIGINAL_CONTENT, mOriginalContent); + // Call the superclass to save the any view hierarchy state + super.onSaveInstanceState(outState); } /** * This method is called when the Activity loses focus. * - * For Activity objects that edit information, onPause() may be the one place where changes are - * saved. The Android application model is predicated on the idea that "save" and "exit" aren't - * required actions. When users navigate away from an Activity, they shouldn't have to go back - * to it to complete their work. The act of going away should save everything and leave the - * Activity in a state where Android can destroy it if necessary. - * - * If the user hasn't done anything, then this deletes or clears out the note, otherwise it - * writes the user's work to the provider. + * While there is no need to override this method in this app, it is shown here to highlight + * that we are not saving any state in onPause, but have moved app state saving to onStop + * callback. + * In earlier versions of this app and popular literature it had been shown that onPause is good + * place to persist any unsaved work, however, this is not really a good practice because of how + * application and process lifecycle behave. + * As a general guideline apps should have a way of saving their business logic that does not + * solely rely on Activity (or other component) lifecyle state transitions. + * As a backstop you should save any app state, not saved during lifetime of the Activity, in + * onStop(). + * For a more detailed explanation of this recommendation please read + * <a href = "https://developer.android.com/guide/topics/processes/process-lifecycle.html"> + * Processes and Application Life Cycle </a>. + * <a href="https://developer.android.com/training/basics/activity-lifecycle/pausing.html"> + * Pausing and Resuming an Activity </a>. */ @Override protected void onPause() { super.onPause(); + } - /* - * Tests to see that the query operation didn't fail (see onCreate()). The Cursor object - * will exist, even if no records were returned, unless the query failed because of some - * exception or error. - * - */ - if (mCursor != null) { + /** + * This method is called when the Activity becomes invisible. + * + * For Activity objects that edit information, onStop() may be the one place where changes maybe + * saved. + * + * If the user hasn't done anything, then this deletes or clears out the note, otherwise it + * writes the user's work to the provider. + */ + @Override + protected void onStop() { + super.onStop(); - // Get the current note text. - String text = mText.getText().toString(); - int length = text.length(); + // Get the current note text. + String text = mText.getText().toString(); + int length = text.length(); /* * If the Activity is in the midst of finishing and there is no text in the current @@ -354,23 +287,22 @@ public class NoteEditor extends Activity { * even if the note was being edited, the assumption being that the user wanted to * "clear out" (delete) the note. */ - if (isFinishing() && (length == 0)) { - setResult(RESULT_CANCELED); - deleteNote(); + if (isFinishing() && (length == 0)) { + setResult(RESULT_CANCELED); + deleteNote(); /* - * Writes the edits to the provider. The note has been edited if an existing note was - * retrieved into the editor *or* if a new note was inserted. In the latter case, - * onCreate() inserted a new empty note into the provider, and it is this new note - * that is being edited. + * Writes the edits to the provider. The note has been edited if an existing note + * was retrieved into the editor *or* if a new note was inserted. + * In the latter case, onCreate() inserted a new empty note into the provider, + * and it is this new note that is being edited. */ - } else if (mState == STATE_EDIT) { - // Creates a map to contain the new values for the columns - updateNote(text, null); - } else if (mState == STATE_INSERT) { - updateNote(text, text); - mState = STATE_EDIT; - } + } else if (mState == STATE_EDIT) { + // Creates a map to contain the new values for the columns + updateNote(text, null); + } else if (mState == STATE_INSERT) { + updateNote(text, text); + mState = STATE_EDIT; } } @@ -409,8 +341,16 @@ public class NoteEditor extends Activity { @Override public boolean onPrepareOptionsMenu(Menu menu) { // Check if note has changed and enable/disable the revert option - int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE); - String savedNote = mCursor.getString(colNoteIndex); + Cursor cursor = getContentResolver().query( + mUri, // The URI for the note that is to be retrieved. + PROJECTION, // The columns to retrieve + null, // No selection criteria are used, so no where columns are needed. + null, // No where columns are used, so no where values are needed. + null // No sort order is needed. + ); + cursor.moveToFirst(); + int colNoteIndex = cursor.getColumnIndex(Notes.COLUMN_NAME_NOTE); + String savedNote = cursor.getString(colNoteIndex); String currentNote = mText.getText().toString(); if (savedNote.equals(currentNote)) { menu.findItem(R.id.menu_revert).setVisible(false); @@ -493,8 +433,8 @@ public class NoteEditor extends Activity { // (moveToFirst() returns true), then this gets the note data from it. if (orig != null) { if (orig.moveToFirst()) { - int colNoteIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE); - int colTitleIndex = mCursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE); + int colNoteIndex = orig.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE); + int colTitleIndex = orig.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE); text = orig.getString(colNoteIndex); title = orig.getString(colTitleIndex); } @@ -571,13 +511,11 @@ public class NoteEditor extends Activity { * android.content.AsyncQueryHandler or android.os.AsyncTask. */ getContentResolver().update( - mUri, // The URI for the record to update. - values, // The map of column names and new values to apply to them. - null, // No selection criteria are used, so no where columns are necessary. - null // No where columns are used, so no where arguments are necessary. - ); - - + mUri, // The URI for the record to update. + values, // The map of column names and new values to apply to them. + null, // No selection criteria are used, so no where columns are necessary. + null // No where columns are used, so no where arguments are necessary. + ); } /** @@ -585,19 +523,17 @@ public class NoteEditor extends Activity { * newly created, or reverts to the original text of the note i */ private final void cancelNote() { - if (mCursor != null) { - if (mState == STATE_EDIT) { - // Put the original note text back into the database - mCursor.close(); - mCursor = null; - ContentValues values = new ContentValues(); - values.put(NotePad.Notes.COLUMN_NAME_NOTE, mOriginalContent); - getContentResolver().update(mUri, values, null, null); - } else if (mState == STATE_INSERT) { - // We inserted an empty note, make sure to delete it - deleteNote(); - } + + if (mState == STATE_EDIT) { + // Put the original note text back into the database + ContentValues values = new ContentValues(); + values.put(NotePad.Notes.COLUMN_NAME_NOTE, mOriginalContent); + getContentResolver().update(mUri, values, null, null); + } else if (mState == STATE_INSERT) { + // We inserted an empty note, make sure to delete it + deleteNote(); } + setResult(RESULT_CANCELED); finish(); } @@ -606,11 +542,50 @@ public class NoteEditor extends Activity { * Take care of deleting a note. Simply deletes the entry. */ private final void deleteNote() { - if (mCursor != null) { - mCursor.close(); - mCursor = null; - getContentResolver().delete(mUri, null, null); - mText.setText(""); + getContentResolver().delete(mUri, null, null); + mText.setText(""); + } + + // LoaderManager callbacks + @Override + public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { + return new CursorLoader( + this, + mUri, // The URI for the note that is to be retrieved. + PROJECTION, // The columns to retrieve + null, // No selection criteria are used, so no where columns are needed. + null, // No where columns are used, so no where values are needed. + null // No sort order is needed. + ); + } + + @Override + public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { + + // Modifies the window title for the Activity according to the current Activity state. + if (cursor != null && cursor.moveToFirst() && mState == STATE_EDIT) { + // Set the title of the Activity to include the note title + int colTitleIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_TITLE); + int colNoteIndex = cursor.getColumnIndex(NotePad.Notes.COLUMN_NAME_NOTE); + + // Gets the title and sets it + String title = cursor.getString(colTitleIndex); + Resources res = getResources(); + String text = String.format(res.getString(R.string.title_edit), title); + setTitle(text); + + // Gets the note text from the Cursor and puts it in the TextView, but doesn't change + // the text cursor's position. + + String note = cursor.getString(colNoteIndex); + mText.setTextKeepState(note); + // Stores the original note text, to allow the user to revert changes. + if (mOriginalContent == null) { + mOriginalContent = note; + } } } + + @Override + public void onLoaderReset(Loader<Cursor> cursorLoader) {} } diff --git a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java index 183964563..f81e22d1f 100644 --- a/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java +++ b/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java @@ -71,11 +71,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C private static HashMap<String, String> sNotesProjectionMap; /** - * A projection map used to select columns from the database - */ - private static HashMap<String, String> sLiveFolderProjectionMap; - - /** * Standard projection for the interesting columns of a normal note. */ private static final String[] READ_NOTE_PROJECTION = new String[] { @@ -96,9 +91,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C // The incoming URI matches the Note ID URI pattern private static final int NOTE_ID = 2; - // The incoming URI matches the Live Folder URI pattern - private static final int LIVE_FOLDER_NOTES = 3; - /** * A UriMatcher instance */ @@ -126,10 +118,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C // to a note ID operation sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID); - // Add a pattern that routes URIs terminated with live_folders/notes to a - // live folder operation - sUriMatcher.addURI(NotePad.AUTHORITY, "live_folders/notes", LIVE_FOLDER_NOTES); - /* * Creates and initializes a projection map that returns all columns */ @@ -155,20 +143,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C sNotesProjectionMap.put( NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE, NotePad.Notes.COLUMN_NAME_MODIFICATION_DATE); - - /* - * Creates an initializes a projection map for handling Live Folders - */ - - // Creates a new projection map instance - sLiveFolderProjectionMap = new HashMap<String, String>(); - - // Maps "_ID" to "_ID AS _ID" for a live folder - sLiveFolderProjectionMap.put(LiveFolders._ID, NotePad.Notes._ID + " AS " + LiveFolders._ID); - - // Maps "NAME" to "title AS NAME" - sLiveFolderProjectionMap.put(LiveFolders.NAME, NotePad.Notes.COLUMN_NAME_TITLE + " AS " + - LiveFolders.NAME); } /** @@ -278,11 +252,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C uri.getPathSegments().get(NotePad.Notes.NOTE_ID_PATH_POSITION)); break; - case LIVE_FOLDER_NOTES: - // If the incoming URI is from a live folder, chooses the live folder projection. - qb.setProjectionMap(sLiveFolderProjectionMap); - break; - default: // If the URI doesn't match any of the known patterns, throw an exception. throw new IllegalArgumentException("Unknown URI " + uri); @@ -339,7 +308,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C // If the pattern is for notes or live folders, returns the general content type. case NOTES: - case LIVE_FOLDER_NOTES: return NotePad.Notes.CONTENT_TYPE; // If the pattern is for note IDs, returns the note ID content type. @@ -380,7 +348,6 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C // If the pattern is for notes or live folders, return null. Data streams are not // supported for this type of URI. case NOTES: - case LIVE_FOLDER_NOTES: return null; // If the pattern is for note IDs and the MIME filter is text/plain, then return @@ -635,7 +602,7 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C throw new IllegalArgumentException("Unknown URI " + uri); } - /*Gets a handle to the content resolver object for the current context, and notifies it + /* Gets a handle to the content resolver object for the current context, and notifies it * that the incoming URI changed. The object passes this along to the resolver framework, * and observers that have registered themselves for the provider are notified. */ @@ -728,7 +695,7 @@ public class NotePadProvider extends ContentProvider implements PipeDataWriter<C throw new IllegalArgumentException("Unknown URI " + uri); } - /*Gets a handle to the content resolver object for the current context, and notifies it + /* Gets a handle to the content resolver object for the current context, and notifies it * that the incoming URI changed. The object passes this along to the resolver framework, * and observers that have registered themselves for the provider are notified. */ diff --git a/samples/NotePad/src/com/example/android/notepad/NotesList.java b/samples/NotePad/src/com/example/android/notepad/NotesList.java index 7e91f646c..bc21a7081 100644 --- a/samples/NotePad/src/com/example/android/notepad/NotesList.java +++ b/samples/NotePad/src/com/example/android/notepad/NotesList.java @@ -16,15 +16,16 @@ package com.example.android.notepad; -import com.example.android.notepad.NotePad; - import android.app.ListActivity; +import android.app.LoaderManager; import android.content.ClipboardManager; import android.content.ClipData; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; +import android.content.CursorLoader; import android.content.Intent; +import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -36,6 +37,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.widget.AdapterView; +import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter; @@ -43,17 +45,14 @@ import android.widget.SimpleCursorAdapter; * Displays a list of notes. Will display notes from the {@link Uri} * provided in the incoming Intent if there is one, otherwise it defaults to displaying the * contents of the {@link NotePadProvider}. - * - * NOTE: Notice that the provider operations in this Activity are taking place on the UI thread. - * This is not a good practice. It is only done here to make the code more readable. A real - * application should use the {@link android.content.AsyncQueryHandler} or - * {@link android.os.AsyncTask} object to perform operations asynchronously on a separate thread. */ -public class NotesList extends ListActivity { +public class NotesList extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> { // For logging and debugging private static final String TAG = "NotesList"; + private static final int LOADER_ID = 0; + /** * The columns needed by the cursor adapter */ @@ -65,6 +64,8 @@ public class NotesList extends ListActivity { /** The index of the title column */ private static final int COLUMN_INDEX_TITLE = 1; + private SimpleCursorAdapter mAdapter; + /** * onCreate is called when Android starts this Activity from scratch. */ @@ -95,18 +96,6 @@ public class NotesList extends ListActivity { */ getListView().setOnCreateContextMenuListener(this); - /* Performs a managed query. The Activity handles closing and requerying the cursor - * when needed. - * - * Please see the introductory note about performing provider operations on the UI thread. - */ - Cursor cursor = managedQuery( - getIntent().getData(), // Use the default content URI for the provider. - PROJECTION, // Return the note ID and title for each note. - null, // No where clause, return all records. - null, // No where clause, therefore no where column values. - NotePad.Notes.DEFAULT_SORT_ORDER // Use the default sort order. - ); /* * The following two arrays create a "map" between columns in the cursor and view IDs @@ -124,17 +113,19 @@ public class NotesList extends ListActivity { int[] viewIDs = { android.R.id.text1 }; // Creates the backing adapter for the ListView. - SimpleCursorAdapter adapter - = new SimpleCursorAdapter( - this, // The Context for the ListView - R.layout.noteslist_item, // Points to the XML for a list item - cursor, // The cursor to get items from - dataColumns, - viewIDs - ); + mAdapter = new SimpleCursorAdapter( + this, // The Context for the ListView + R.layout.noteslist_item, // Points to the XML for a list item + null, // The cursor is set by CursorLoader when loaded + dataColumns, + viewIDs, + CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER + ); // Sets the ListView's adapter to be the cursor adapter that was just created. - setListAdapter(adapter); + setListAdapter(mAdapter); + // Initialize the LoaderManager and start the query + getLoaderManager().initLoader(LOADER_ID, null, this); } /** @@ -464,4 +455,28 @@ public class NotesList extends ListActivity { startActivity(new Intent(Intent.ACTION_EDIT, uri)); } } + + // LoaderManager callbacks + @Override + public Loader<Cursor> onCreateLoader(int i, Bundle bundle) { + return new CursorLoader( + this, + getIntent().getData(), // Use the default content URI for the provider. + PROJECTION, // Return the note ID and title for each note. + null, // No where clause, return all records. + null, // No where clause, therefore no where column values. + NotePad.Notes.DEFAULT_SORT_ORDER // Use the default sort order. + ); + } + + @Override + public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) { + mAdapter.changeCursor(cursor); + } + + @Override + public void onLoaderReset(Loader<Cursor> cursorLoader) { + // Since the Loader is reset, this removes the cursor reference from the adapter. + mAdapter.changeCursor(null); + } } diff --git a/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java b/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java deleted file mode 100644 index 24afaa0d4..000000000 --- a/samples/NotePad/src/com/example/android/notepad/NotesLiveFolder.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * 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.example.android.notepad; - -import com.example.android.notepad.NotePad; - -import android.app.Activity; -import android.content.Intent; -import android.content.Intent.ShortcutIconResource; -import android.os.Bundle; -import android.provider.LiveFolders; - -/** - * This Activity creates a live folder Intent and - * sends it back to HOME. From the data in the Intent, HOME creates a live folder and displays - * its icon in the Home view. - * When the user clicks the icon, Home uses the data it got from the Intent to retrieve information - * from a content provider and display it in a View. - * - * The intent filter for this Activity is set to ACTION_CREATE_LIVE_FOLDER, which - * HOME sends in response to a long press and selection of Live Folder. - */ -public class NotesLiveFolder extends Activity { - - /** - * All of the work is done in onCreate(). The Activity doesn't actually display a UI. - * Instead, it sets up an Intent and returns it to its caller (the HOME activity). - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - /* - * Gets the incoming Intent and its action. If the incoming Intent was - * ACTION_CREATE_LIVE_FOLDER, then create an outgoing Intent with the - * necessary data and send back OK. Otherwise, send back CANCEL. - */ - final Intent intent = getIntent(); - final String action = intent.getAction(); - - if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) { - - // Creates a new Intent. - final Intent liveFolderIntent = new Intent(); - - /* - * The following statements put data into the outgoing Intent. Please see - * {@link android.provider.LiveFolders for a detailed description of these - * data values. From this data, HOME sets up a live folder. - */ - // Sets the URI pattern for the content provider backing the folder. - liveFolderIntent.setData(NotePad.Notes.LIVE_FOLDER_URI); - - // Adds the display name of the live folder as an Extra string. - String foldername = getString(R.string.live_folder_name); - liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, foldername); - - // Adds the display icon of the live folder as an Extra resource. - ShortcutIconResource foldericon = - Intent.ShortcutIconResource.fromContext(this, R.drawable.live_folder_notes); - liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON, foldericon); - - // Add the display mode of the live folder as an integer. The specified - // mode causes the live folder to display as a list. - liveFolderIntent.putExtra( - LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, - LiveFolders.DISPLAY_MODE_LIST); - - /* - * Adds a base action for items in the live folder list, as an Intent. When the - * user clicks an individual note in the list, the live folder fires this Intent. - * - * Its action is ACTION_EDIT, so it triggers the Note Editor activity. Its - * data is the URI pattern for a single note identified by its ID. The live folder - * automatically adds the ID value of the selected item to the URI pattern. - * - * As a result, Note Editor is triggered and gets a single note to retrieve by ID. - */ - Intent returnIntent - = new Intent(Intent.ACTION_EDIT, NotePad.Notes.CONTENT_ID_URI_PATTERN); - liveFolderIntent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, returnIntent); - - /* Creates an ActivityResult object to propagate back to HOME. Set its result indicator - * to OK, and sets the returned Intent to the live folder Intent that was just - * constructed. - */ - setResult(RESULT_OK, liveFolderIntent); - - } else { - - // If the original action was not ACTION_CREATE_LIVE_FOLDER, creates an - // ActivityResult with the indicator set to CANCELED, but do not return an Intent - setResult(RESULT_CANCELED); - } - - // Closes the Activity. The ActivityObject is propagated back to the caller. - finish(); - } -} diff --git a/samples/NotePad/src/com/example/android/notepad/TitleEditor.java b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java index 5abe97b39..e6f029b98 100644 --- a/samples/NotePad/src/com/example/android/notepad/TitleEditor.java +++ b/samples/NotePad/src/com/example/android/notepad/TitleEditor.java @@ -21,8 +21,10 @@ import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; +import android.text.TextUtils; import android.view.View; import android.widget.EditText; +import android.widget.Toast; /** * This Activity allows the user to edit a note's title. It displays a floating window @@ -49,15 +51,15 @@ public class TitleEditor extends Activity { // The position of the title column in a Cursor returned by the provider. private static final int COLUMN_INDEX_TITLE = 1; - // A Cursor object that will contain the results of querying the provider for a note. - private Cursor mCursor; - // An EditText object for preserving the edited title. private EditText mText; // A URI object for the note whose title is being edited. private Uri mUri; + // The title that was last saved. + private String mSavedTitle; + /** * This method is called by Android when the Activity is first started. From the incoming * Intent, it determines what kind of editing is desired, and then does it. @@ -69,6 +71,9 @@ public class TitleEditor extends Activity { // Set the View for this Activity object's UI. setContentView(R.layout.title_editor); + // Gets the View ID for the EditText box + mText = (EditText) this.findViewById(R.id.title); + // Get the Intent that activated this Activity, and from it get the URI of the note whose // title we need to edit. mUri = getIntent().getData(); @@ -82,7 +87,7 @@ public class TitleEditor extends Activity { * android.content.AsyncQueryHandler or android.os.AsyncTask. */ - mCursor = managedQuery( + Cursor cursor = getContentResolver().query( mUri, // The URI for the note that is to be retrieved. PROJECTION, // The columns to retrieve null, // No selection criteria are used, so no where columns are needed. @@ -90,8 +95,15 @@ public class TitleEditor extends Activity { null // No sort order is needed. ); - // Gets the View ID for the EditText box - mText = (EditText) this.findViewById(R.id.title); + if (cursor != null) { + + // The Cursor was just retrieved, so its index is set to one record *before* the first + // record retrieved. This moves it to the first record. + cursor.moveToFirst(); + + // Displays the current title text in the EditText object. + mText.setText(cursor.getString(COLUMN_INDEX_TITLE)); + } } /** @@ -103,65 +115,83 @@ public class TitleEditor extends Activity { @Override protected void onResume() { super.onResume(); - - // Verifies that the query made in onCreate() actually worked. If it worked, then the - // Cursor object is not null. If it is *empty*, then mCursor.getCount() == 0. - if (mCursor != null) { - - // The Cursor was just retrieved, so its index is set to one record *before* the first - // record retrieved. This moves it to the first record. - mCursor.moveToFirst(); - - // Displays the current title text in the EditText object. - mText.setText(mCursor.getString(COLUMN_INDEX_TITLE)); - } } /** * This method is called when the Activity loses focus. * - * For Activity objects that edit information, onPause() may be the one place where changes are - * saved. The Android application model is predicated on the idea that "save" and "exit" aren't - * required actions. When users navigate away from an Activity, they shouldn't have to go back - * to it to complete their work. The act of going away should save everything and leave the - * Activity in a state where Android can destroy it if necessary. - * - * Updates the note with the text currently in the text box. + * While there is no need to override this method in this app, it is shown here to highlight + * that we are not saving any state in onPause, but have moved app state saving to onStop + * callback. + * In earlier versions of this app and popular literature it had been shown that onPause is good + * place to persist any unsaved work, however, this is not really a good practice because of how + * application and process lifecycle behave. + * As a general guideline apps should have a way of saving their business logic that does not + * solely rely on Activity (or other component) lifecyle state transitions. + * As a backstop you should save any app state, not saved during lifetime of the Activity, in + * onStop(). + * For a more detailed explanation of this recommendation please read + * <a href = "https://developer.android.com/guide/topics/processes/process-lifecycle.html"> + * Processes and Application Life Cycle </a>. + * <a href="https://developer.android.com/training/basics/activity-lifecycle/pausing.html"> + * Pausing and Resuming an Activity </a>. */ @Override protected void onPause() { super.onPause(); + } - // Verifies that the query made in onCreate() actually worked. If it worked, then the - // Cursor object is not null. If it is *empty*, then mCursor.getCount() == 0. - - if (mCursor != null) { - - // Creates a values map for updating the provider. - ContentValues values = new ContentValues(); - - // In the values map, sets the title to the current contents of the edit box. - values.put(NotePad.Notes.COLUMN_NAME_TITLE, mText.getText().toString()); - - /* - * Updates the provider with the note's new title. - * - * Note: This is being done on the UI thread. It will block the thread until the - * update completes. In a sample app, going against a simple provider based on a - * local database, the block will be momentary, but in a real app you should use - * android.content.AsyncQueryHandler or android.os.AsyncTask. - */ - getContentResolver().update( - mUri, // The URI for the note to update. - values, // The values map containing the columns to update and the values to use. - null, // No selection criteria is used, so no "where" columns are needed. - null // No "where" columns are used, so no "where" values are needed. - ); - - } + /** + * This method is called when the Activity becomes invisible. + * + * For Activity objects that edit information, onStop() may be the one place where changes are + * saved. + * Updates the note with the text currently in the text box. + */ + @Override + protected void onStop() { + super.onStop(); + saveTitle(); } public void onClickOk(View v) { + saveTitle(); finish(); } + + // Saves the title if required + private void saveTitle() { + + if (!TextUtils.isEmpty(mText.getText())) { + + String newTitle = mText.getText().toString(); + + if (!newTitle.equals(mSavedTitle)) { + // Creates a values map for updating the provider. + ContentValues values = new ContentValues(); + + // In the values map, sets the title to the current contents of the edit box. + values.put(NotePad.Notes.COLUMN_NAME_TITLE, newTitle); + + /* + * Updates the provider with the note's new title. + * + * Note: This is being done on the UI thread. It will block the thread until the + * update completes. In a sample app, going against a simple provider based on a + * local database, the block will be momentary, but in a real app you should use + * android.content.AsyncQueryHandler or android.os.AsyncTask. + */ + getContentResolver().update( + mUri, // The URI for the note to update. + values, + // The values map containing the columns to update and the values to use. + null, // No selection criteria is used, so no "where" columns are needed. + null // No "where" columns are used, so no "where" values are needed. + ); + mSavedTitle = newTitle; + } + } else { + Toast.makeText(this, R.string.title_blank, Toast.LENGTH_SHORT).show(); + } + } } diff --git a/samples/browseable/AccelerometerPlay/AndroidManifest.xml b/samples/browseable/AccelerometerPlay/AndroidManifest.xml deleted file mode 100644 index 3c3ec2743..000000000 --- a/samples/browseable/AccelerometerPlay/AndroidManifest.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - android:versionCode="1" - android:versionName="1.0" package="com.example.android.accelerometerplay"> - <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name"> - <activity android:name=".AccelerometerPlayActivity" - android:label="@string/app_name" - android:screenOrientation="portrait" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - - </application> - - -<uses-sdk android:minSdkVersion="5"></uses-sdk> -<uses-permission android:name="android.permission.VIBRATE"></uses-permission> - -<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission> - -</manifest> diff --git a/samples/browseable/AccelerometerPlay/_index.jd b/samples/browseable/AccelerometerPlay/_index.jd deleted file mode 100644 index 5e09e2d14..000000000 --- a/samples/browseable/AccelerometerPlay/_index.jd +++ /dev/null @@ -1,15 +0,0 @@ - -page.tags="AccelerometerPlay" -sample.group=Sensors -@jd:body - -<p> - - <p>This sample demonstrates how to use an accelerometer sensor as input for - a physics-based view. The input from the accelerometer is used to simulate a - virtual surface, and a number of free-moving objects placed on top of it.</p> - - <p>Any effects from the device's acceleration vector (including both gravity and - temporary movement) will be translated to the on-screen particles.</p> - - </p> diff --git a/samples/browseable/AccelerometerPlay/res/drawable-hdpi/ball.png b/samples/browseable/AccelerometerPlay/res/drawable-hdpi/ball.png Binary files differdeleted file mode 100644 index e79e4d615..000000000 --- a/samples/browseable/AccelerometerPlay/res/drawable-hdpi/ball.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/drawable-hdpi/wood.jpg b/samples/browseable/AccelerometerPlay/res/drawable-hdpi/wood.jpg Binary files differdeleted file mode 100644 index 883f491d9..000000000 --- a/samples/browseable/AccelerometerPlay/res/drawable-hdpi/wood.jpg +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/layout/main.xml b/samples/browseable/AccelerometerPlay/res/layout/main.xml deleted file mode 100644 index c69b22294..000000000 --- a/samples/browseable/AccelerometerPlay/res/layout/main.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:background="@drawable/wood" - > -</FrameLayout> diff --git a/samples/browseable/AccelerometerPlay/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/AccelerometerPlay/res/mipmap-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 800c55629..000000000 --- a/samples/browseable/AccelerometerPlay/res/mipmap-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/AccelerometerPlay/res/mipmap-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 1d6e9e55f..000000000 --- a/samples/browseable/AccelerometerPlay/res/mipmap-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/AccelerometerPlay/res/mipmap-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 2989356b5..000000000 --- a/samples/browseable/AccelerometerPlay/res/mipmap-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/AccelerometerPlay/res/mipmap-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 56b87a28d..000000000 --- a/samples/browseable/AccelerometerPlay/res/mipmap-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/AccelerometerPlay/res/mipmap-xxxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 500543319..000000000 --- a/samples/browseable/AccelerometerPlay/res/mipmap-xxxhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AccelerometerPlay/res/values/strings.xml b/samples/browseable/AccelerometerPlay/res/values/strings.xml deleted file mode 100644 index 6e3785e94..000000000 --- a/samples/browseable/AccelerometerPlay/res/values/strings.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - 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. ---> - -<resources> - <string name="app_name">AccelerometerPlay</string> -</resources> diff --git a/samples/browseable/AccelerometerPlay/src/com.example.android.accelerometerplay/AccelerometerPlayActivity.java b/samples/browseable/AccelerometerPlay/src/com.example.android.accelerometerplay/AccelerometerPlayActivity.java deleted file mode 100644 index b15685261..000000000 --- a/samples/browseable/AccelerometerPlay/src/com.example.android.accelerometerplay/AccelerometerPlayActivity.java +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * 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.example.android.accelerometerplay; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.BitmapFactory.Options; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Build; -import android.os.Bundle; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.Display; -import android.view.Surface; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.FrameLayout; - -/** - * This is an example of using the accelerometer to integrate the device's - * acceleration to a position using the Verlet method. This is illustrated with - * a very simple particle system comprised of a few iron balls freely moving on - * an inclined wooden table. The inclination of the virtual table is controlled - * by the device's accelerometer. - * - * @see SensorManager - * @see SensorEvent - * @see Sensor - */ - -public class AccelerometerPlayActivity extends Activity { - - private SimulationView mSimulationView; - private SensorManager mSensorManager; - private PowerManager mPowerManager; - private WindowManager mWindowManager; - private Display mDisplay; - private WakeLock mWakeLock; - - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // Get an instance of the SensorManager - mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); - - // Get an instance of the PowerManager - mPowerManager = (PowerManager) getSystemService(POWER_SERVICE); - - // Get an instance of the WindowManager - mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); - mDisplay = mWindowManager.getDefaultDisplay(); - - // Create a bright wake lock - mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass() - .getName()); - - // instantiate our simulation view and set it as the activity's content - mSimulationView = new SimulationView(this); - mSimulationView.setBackgroundResource(R.drawable.wood); - setContentView(mSimulationView); - } - - @Override - protected void onResume() { - super.onResume(); - /* - * when the activity is resumed, we acquire a wake-lock so that the - * screen stays on, since the user will likely not be fiddling with the - * screen or buttons. - */ - mWakeLock.acquire(); - - // Start the simulation - mSimulationView.startSimulation(); - } - - @Override - protected void onPause() { - super.onPause(); - /* - * When the activity is paused, we make sure to stop the simulation, - * release our sensor resources and wake locks - */ - - // Stop the simulation - mSimulationView.stopSimulation(); - - // and release our wake-lock - mWakeLock.release(); - } - - class SimulationView extends FrameLayout implements SensorEventListener { - // diameter of the balls in meters - private static final float sBallDiameter = 0.004f; - private static final float sBallDiameter2 = sBallDiameter * sBallDiameter; - - private final int mDstWidth; - private final int mDstHeight; - - private Sensor mAccelerometer; - private long mLastT; - - private float mXDpi; - private float mYDpi; - private float mMetersToPixelsX; - private float mMetersToPixelsY; - private float mXOrigin; - private float mYOrigin; - private float mSensorX; - private float mSensorY; - private float mHorizontalBound; - private float mVerticalBound; - private final ParticleSystem mParticleSystem; - /* - * Each of our particle holds its previous and current position, its - * acceleration. for added realism each particle has its own friction - * coefficient. - */ - class Particle extends View { - private float mPosX = (float) Math.random(); - private float mPosY = (float) Math.random(); - private float mVelX; - private float mVelY; - - public Particle(Context context) { - super(context); - } - - public Particle(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public Particle(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public Particle(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - public void computePhysics(float sx, float sy, float dT) { - - final float ax = -sx/5; - final float ay = -sy/5; - - mPosX += mVelX * dT + ax * dT * dT / 2; - mPosY += mVelY * dT + ay * dT * dT / 2; - - mVelX += ax * dT; - mVelY += ay * dT; - } - - /* - * Resolving constraints and collisions with the Verlet integrator - * can be very simple, we simply need to move a colliding or - * constrained particle in such way that the constraint is - * satisfied. - */ - public void resolveCollisionWithBounds() { - final float xmax = mHorizontalBound; - final float ymax = mVerticalBound; - final float x = mPosX; - final float y = mPosY; - if (x > xmax) { - mPosX = xmax; - mVelX = 0; - } else if (x < -xmax) { - mPosX = -xmax; - mVelX = 0; - } - if (y > ymax) { - mPosY = ymax; - mVelY = 0; - } else if (y < -ymax) { - mPosY = -ymax; - mVelY = 0; - } - } - } - - /* - * A particle system is just a collection of particles - */ - class ParticleSystem { - static final int NUM_PARTICLES = 5; - private Particle mBalls[] = new Particle[NUM_PARTICLES]; - - ParticleSystem() { - /* - * Initially our particles have no speed or acceleration - */ - for (int i = 0; i < mBalls.length; i++) { - mBalls[i] = new Particle(getContext()); - mBalls[i].setBackgroundResource(R.drawable.ball); - mBalls[i].setLayerType(LAYER_TYPE_HARDWARE, null); - addView(mBalls[i], new ViewGroup.LayoutParams(mDstWidth, mDstHeight)); - } - } - - /* - * Update the position of each particle in the system using the - * Verlet integrator. - */ - private void updatePositions(float sx, float sy, long timestamp) { - final long t = timestamp; - if (mLastT != 0) { - final float dT = (float) (t - mLastT) / 1000.f /** (1.0f / 1000000000.0f)*/; - final int count = mBalls.length; - for (int i = 0; i < count; i++) { - Particle ball = mBalls[i]; - ball.computePhysics(sx, sy, dT); - } - } - mLastT = t; - } - - /* - * Performs one iteration of the simulation. First updating the - * position of all the particles and resolving the constraints and - * collisions. - */ - public void update(float sx, float sy, long now) { - // update the system's positions - updatePositions(sx, sy, now); - - // We do no more than a limited number of iterations - final int NUM_MAX_ITERATIONS = 10; - - /* - * Resolve collisions, each particle is tested against every - * other particle for collision. If a collision is detected the - * particle is moved away using a virtual spring of infinite - * stiffness. - */ - boolean more = true; - final int count = mBalls.length; - for (int k = 0; k < NUM_MAX_ITERATIONS && more; k++) { - more = false; - for (int i = 0; i < count; i++) { - Particle curr = mBalls[i]; - for (int j = i + 1; j < count; j++) { - Particle ball = mBalls[j]; - float dx = ball.mPosX - curr.mPosX; - float dy = ball.mPosY - curr.mPosY; - float dd = dx * dx + dy * dy; - // Check for collisions - if (dd <= sBallDiameter2) { - /* - * add a little bit of entropy, after nothing is - * perfect in the universe. - */ - dx += ((float) Math.random() - 0.5f) * 0.0001f; - dy += ((float) Math.random() - 0.5f) * 0.0001f; - dd = dx * dx + dy * dy; - // simulate the spring - final float d = (float) Math.sqrt(dd); - final float c = (0.5f * (sBallDiameter - d)) / d; - final float effectX = dx * c; - final float effectY = dy * c; - curr.mPosX -= effectX; - curr.mPosY -= effectY; - ball.mPosX += effectX; - ball.mPosY += effectY; - more = true; - } - } - curr.resolveCollisionWithBounds(); - } - } - } - - public int getParticleCount() { - return mBalls.length; - } - - public float getPosX(int i) { - return mBalls[i].mPosX; - } - - public float getPosY(int i) { - return mBalls[i].mPosY; - } - } - - public void startSimulation() { - /* - * It is not necessary to get accelerometer events at a very high - * rate, by using a slower rate (SENSOR_DELAY_UI), we get an - * automatic low-pass filter, which "extracts" the gravity component - * of the acceleration. As an added benefit, we use less power and - * CPU resources. - */ - mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); - } - - public void stopSimulation() { - mSensorManager.unregisterListener(this); - } - - public SimulationView(Context context) { - super(context); - mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - mXDpi = metrics.xdpi; - mYDpi = metrics.ydpi; - mMetersToPixelsX = mXDpi / 0.0254f; - mMetersToPixelsY = mYDpi / 0.0254f; - - // rescale the ball so it's about 0.5 cm on screen - mDstWidth = (int) (sBallDiameter * mMetersToPixelsX + 0.5f); - mDstHeight = (int) (sBallDiameter * mMetersToPixelsY + 0.5f); - mParticleSystem = new ParticleSystem(); - - Options opts = new Options(); - opts.inDither = true; - opts.inPreferredConfig = Bitmap.Config.RGB_565; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - // compute the origin of the screen relative to the origin of - // the bitmap - mXOrigin = (w - mDstWidth) * 0.5f; - mYOrigin = (h - mDstHeight) * 0.5f; - mHorizontalBound = ((w / mMetersToPixelsX - sBallDiameter) * 0.5f); - mVerticalBound = ((h / mMetersToPixelsY - sBallDiameter) * 0.5f); - } - - @Override - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) - return; - /* - * record the accelerometer data, the event's timestamp as well as - * the current time. The latter is needed so we can calculate the - * "present" time during rendering. In this application, we need to - * take into account how the screen is rotated with respect to the - * sensors (which always return data in a coordinate space aligned - * to with the screen in its native orientation). - */ - - switch (mDisplay.getRotation()) { - case Surface.ROTATION_0: - mSensorX = event.values[0]; - mSensorY = event.values[1]; - break; - case Surface.ROTATION_90: - mSensorX = -event.values[1]; - mSensorY = event.values[0]; - break; - case Surface.ROTATION_180: - mSensorX = -event.values[0]; - mSensorY = -event.values[1]; - break; - case Surface.ROTATION_270: - mSensorX = event.values[1]; - mSensorY = -event.values[0]; - break; - } - } - - @Override - protected void onDraw(Canvas canvas) { - /* - * Compute the new position of our object, based on accelerometer - * data and present time. - */ - final ParticleSystem particleSystem = mParticleSystem; - final long now = System.currentTimeMillis(); - final float sx = mSensorX; - final float sy = mSensorY; - - particleSystem.update(sx, sy, now); - - final float xc = mXOrigin; - final float yc = mYOrigin; - final float xs = mMetersToPixelsX; - final float ys = mMetersToPixelsY; - final int count = particleSystem.getParticleCount(); - for (int i = 0; i < count; i++) { - /* - * We transform the canvas so that the coordinate system matches - * the sensors coordinate system with the origin in the center - * of the screen and the unit is the meter. - */ - final float x = xc + particleSystem.getPosX(i) * xs; - final float y = yc - particleSystem.getPosY(i) * ys; - particleSystem.mBalls[i].setTranslationX(x); - particleSystem.mBalls[i].setTranslationY(y); - } - - // and make sure to redraw asap - invalidate(); - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - } -} diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd b/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd deleted file mode 100644 index d9df53688..000000000 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/_index.jd +++ /dev/null @@ -1,11 +0,0 @@ - -page.tags="ActionBarCompat-ShareActionProvider" -sample.group=UI -@jd:body - -<p> - - This sample shows you how a provide a context-sensitive ShareActionProvider with - ActionBarCompat, backwards compatible to API v7. - - </p> diff --git a/samples/browseable/ActiveNotifications/res/values/strings.xml b/samples/browseable/ActiveNotifications/res/values/strings.xml index a4275fdf2..783a0744b 100644 --- a/samples/browseable/ActiveNotifications/res/values/strings.xml +++ b/samples/browseable/ActiveNotifications/res/values/strings.xml @@ -16,8 +16,8 @@ --> <resources> <string name="add_a_notification">Add a notification</string> - <string name="active_notifications">Active Notifications: %1$s</string> + <string name="active_notifications">Active Notifications: %1$d</string> <string name="update_notification_count">Update count</string> <string name="sample_notification_content">This is a sample notification.</string> - <string name="sample_notification_summary_content">There are %s ActiveNotifications.</string> + <string name="sample_notification_summary_content">There are %d ActiveNotifications.</string> </resources> diff --git a/samples/browseable/ActiveNotifications/src/com.example.android.activenotifications/ActiveNotificationsFragment.java b/samples/browseable/ActiveNotifications/src/com.example.android.activenotifications/ActiveNotificationsFragment.java index db0dbac6f..d752a36f2 100644 --- a/samples/browseable/ActiveNotifications/src/com.example.android.activenotifications/ActiveNotificationsFragment.java +++ b/samples/browseable/ActiveNotifications/src/com.example.android.activenotifications/ActiveNotificationsFragment.java @@ -129,23 +129,12 @@ public class ActiveNotificationsFragment extends Fragment { * Adds/updates/removes the notification summary as necessary. */ protected void updateNotificationSummary() { - final StatusBarNotification[] activeNotifications = mNotificationManager - .getActiveNotifications(); - - int numberOfNotifications = activeNotifications.length; - // Since the notifications might include a summary notification remove it from the count if - // it is present. - for (StatusBarNotification notification : activeNotifications) { - if (notification.getId() == NOTIFICATION_GROUP_SUMMARY_ID) { - numberOfNotifications--; - break; - } - } + int numberOfNotifications = getNumberOfNotifications(); if (numberOfNotifications > 1) { // Add/update the notification summary. String notificationContent = getString(R.string.sample_notification_summary_content, - "" + numberOfNotifications); + numberOfNotifications); final NotificationCompat.Builder builder = new NotificationCompat.Builder(getActivity()) .setSmallIcon(R.mipmap.ic_notification) .setStyle(new NotificationCompat.BigTextStyle() @@ -165,12 +154,7 @@ public class ActiveNotificationsFragment extends Fragment { * display them to the user. */ protected void updateNumberOfNotifications() { - // [BEGIN get_active_notifications] - // Query the currently displayed notifications. - final StatusBarNotification[] activeNotifications = mNotificationManager - .getActiveNotifications(); - // [END get_active_notifications] - final int numberOfNotifications = activeNotifications.length; + final int numberOfNotifications = getNumberOfNotifications(); mNumberOfNotifications.setText(getString(R.string.active_notifications, numberOfNotifications)); Log.i(TAG, getString(R.string.active_notifications, numberOfNotifications)); @@ -190,4 +174,21 @@ public class ActiveNotificationsFragment extends Fragment { } return notificationId; } + + private int getNumberOfNotifications() { + // [BEGIN get_active_notifications] + // Query the currently displayed notifications. + final StatusBarNotification[] activeNotifications = mNotificationManager + .getActiveNotifications(); + // [END get_active_notifications] + + // Since the notifications might include a summary notification remove it from the count if + // it is present. + for (StatusBarNotification notification : activeNotifications) { + if (notification.getId() == NOTIFICATION_GROUP_SUMMARY_ID) { + return activeNotifications.length - 1; + } + } + return activeNotifications.length; + } } diff --git a/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml b/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml index 2c74e83f0..c59b3b393 100644 --- a/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml +++ b/samples/browseable/AdvancedImmersiveMode/res/layout/fragment_flags.xml @@ -1,43 +1,44 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" android:layout_width="match_parent" + android:orientation="vertical" + android:layout_width="match_parent" android:layout_height="wrap_content"> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/flag_enable_lowprof" - android:text="Enable Low Profile Mode" /> + android:text="@string/enable_low_profile_mode" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/flag_hide_navbar" - android:text="Hide Navigation bar" /> + android:text="@string/hide_navigation_bar" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/flag_hide_statbar" - android:text="Hide Status Bar" /> + android:text="@string/hide_status_bar" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/flag_enable_immersive" - android:text="Enable Immersive Mode" /> + android:text="@string/enable_immersive_mode" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/flag_enable_immersive_sticky" - android:text="Enable Immersive Mode (Sticky)" /> + android:text="@string/enable_immersive_mode_sticky" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Do things!" + android:text="@string/do_things" android:id="@+id/btn_changeFlags" /> @@ -45,25 +46,24 @@ android:layout_marginTop="@dimen/margin_large" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Common flag presets"/> - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" android:layout_width="match_parent" + android:text="@string/common_flag_presets" /> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Immersive Mode" + android:text="@string/immersive_mode" android:id="@+id/btn_immersive" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Leanback Mode" + android:text="@string/leanback_mode" android:id="@+id/btn_leanback" /> </LinearLayout> - - - </LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml new file mode 100644 index 000000000..6cfd28c78 --- /dev/null +++ b/samples/browseable/AdvancedImmersiveMode/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?><!-- + Copyright 2016 The Android Open Source Project + + 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. +--> + +<resources> + <string name="enable_low_profile_mode">Enable Low Profile Mode</string> + <string name="hide_navigation_bar">Hide Navigation bar</string> + <string name="hide_status_bar">Hide Status Bar</string> + <string name="enable_immersive_mode">Enable Immersive Mode</string> + <string name="enable_immersive_mode_sticky">Enable Immersive Mode (Sticky)</string> + <string name="do_things">Do things!</string> + <string name="common_flag_presets">Common flag presets</string> + <string name="immersive_mode">Immersive Mode</string> + <string name="leanback_mode">Leanback Mode</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/AgendaData/Application/AndroidManifest.xml b/samples/browseable/AgendaData/Application/AndroidManifest.xml index ad6cccde8..083b223dd 100644 --- a/samples/browseable/AgendaData/Application/AndroidManifest.xml +++ b/samples/browseable/AgendaData/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.agendadata"> <uses-sdk android:minSdkVersion="18" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <!-- BEGIN_INCLUDE(manifest) --> diff --git a/samples/browseable/AgendaData/Wearable/AndroidManifest.xml b/samples/browseable/AgendaData/Wearable/AndroidManifest.xml index 87bed7061..f8bbdd80b 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" > <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -28,6 +28,10 @@ > <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + + <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> diff --git a/samples/browseable/AlwaysOn/AndroidManifest.xml b/samples/browseable/AlwaysOn/AndroidManifest.xml index 12c4b03a5..c105808c9 100644 --- a/samples/browseable/AlwaysOn/AndroidManifest.xml +++ b/samples/browseable/AlwaysOn/AndroidManifest.xml @@ -25,7 +25,12 @@ <application android:allowBackup="false" - android:label="@string/app_name"> + android:label="@string/app_name" + android:theme="@android:style/Theme.DeviceDefault"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> <!--If you want your app to run on pre-22, then set required to false --> <uses-library android:name="com.google.android.wearable" android:required="false" /> diff --git a/samples/browseable/AppRestrictionEnforcer/AndroidManifest.xml b/samples/browseable/AppRestrictionEnforcer/AndroidManifest.xml index c66b4b33e..1cd6ae851 100644 --- a/samples/browseable/AppRestrictionEnforcer/AndroidManifest.xml +++ b/samples/browseable/AppRestrictionEnforcer/AndroidManifest.xml @@ -16,15 +16,17 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.example.android.apprestrictionenforcer" android:versionCode="1" android:versionName="1.0"> <application android:allowBackup="true" - android:icon="@drawable/ic_launcher" + android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + tools:ignore="GoogleAppIndexingWarning"> <activity android:name=".MainActivity" diff --git a/samples/browseable/AppRestrictionEnforcer/_index.jd b/samples/browseable/AppRestrictionEnforcer/_index.jd index ec7be7ccf..632cee2af 100644 --- a/samples/browseable/AppRestrictionEnforcer/_index.jd +++ b/samples/browseable/AppRestrictionEnforcer/_index.jd @@ -6,6 +6,6 @@ sample.group=Admin <p> This sample demonstrates how to set restrictions to other apps as a profile owner. - Use AppRestrictionSchema sample as a app with available restrictions. + Use the AppRestrictionSchema sample to set restrictions. </p> diff --git a/samples/browseable/AppRestrictionEnforcer/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index e7bd161ef..000000000 --- a/samples/browseable/AppRestrictionEnforcer/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionEnforcer/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 4c42c9c45..000000000 --- a/samples/browseable/AppRestrictionEnforcer/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionEnforcer/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 3ec336888..000000000 --- a/samples/browseable/AppRestrictionEnforcer/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionEnforcer/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 730f80cd2..000000000 --- a/samples/browseable/AppRestrictionEnforcer/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml index 8fde91fc3..c21939514 100644 --- a/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml +++ b/samples/browseable/AppRestrictionEnforcer/res/layout/fragment_setup_profile.xml @@ -18,7 +18,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="com.example.android.basicmanagedprofile.MainActivity.MainFragment"> + tools:context="com.example.android.apprestrictionenforcer.MainActivity"> <LinearLayout android:layout_width="match_parent" diff --git a/samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml b/samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml deleted file mode 100644 index 6927d801c..000000000 --- a/samples/browseable/AppRestrictionEnforcer/res/layout/separator.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2014 The Android Open Source Project - -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. ---> -<View xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_marginBottom="@dimen/margin_medium" - android:layout_marginTop="@dimen/margin_medium" - android:background="#9000"/> diff --git a/samples/browseable/AppRestrictionEnforcer/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..7e0b8c8c3 --- /dev/null +++ b/samples/browseable/AppRestrictionEnforcer/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionEnforcer/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..963c36e1e --- /dev/null +++ b/samples/browseable/AppRestrictionEnforcer/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionEnforcer/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..6e0f52d5d --- /dev/null +++ b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..3ff049c1b --- /dev/null +++ b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..9bf83de04 --- /dev/null +++ b/samples/browseable/AppRestrictionEnforcer/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionEnforcer/res/values/base-strings.xml b/samples/browseable/AppRestrictionEnforcer/res/values/base-strings.xml index dbf51bdf6..9e3dc41da 100644 --- a/samples/browseable/AppRestrictionEnforcer/res/values/base-strings.xml +++ b/samples/browseable/AppRestrictionEnforcer/res/values/base-strings.xml @@ -22,7 +22,7 @@ This sample demonstrates how to set restrictions to other apps as a profile owner. - Use AppRestrictionSchema sample as a app with available restrictions. + Use the AppRestrictionSchema sample to set restrictions. ]]> diff --git a/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml b/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml index 07e1c85c7..88fe8cc73 100644 --- a/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml +++ b/samples/browseable/AppRestrictionEnforcer/res/values/strings.xml @@ -22,8 +22,6 @@ <string name="status_need_reinstall">AppRestrictionSchema needs reinstalling.</string> <string name="unhide">Activate AppRestrictionSchema</string> <string name="allow_saying_hello">Allow AppRestrictionSchema to say hello: </string> - <string name="allowed">Allowed</string> - <string name="disallowed">Disallowed</string> <string name="profile_name">AppRestrictionEnforcer </string> <string name="message">Message: </string> <string name="number">Number: </string> diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java index bb4e958c7..0f8a1e34b 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/Constants.java @@ -21,7 +21,6 @@ public interface Constants { /** * Package name of the AppRestrictionSchema sample. */ - public static final String PACKAGE_NAME_APP_RESTRICTION_SCHEMA - = "com.example.android.apprestrictionschema"; + String PACKAGE_NAME_APP_RESTRICTION_SCHEMA = "com.example.android.apprestrictionschema"; } diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/ItemAddFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/ItemAddFragment.java index 091a0a889..a5f6628a8 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/ItemAddFragment.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/ItemAddFragment.java @@ -16,7 +16,6 @@ package com.example.android.apprestrictionenforcer; -import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java index 85eace96b..e250e7417 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/MainActivity.java @@ -20,6 +20,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.support.v4.app.FragmentActivity; @@ -38,9 +39,16 @@ public class MainActivity extends FragmentActivity implements StatusFragment.Sta showSetupProfile(); } else { try { + int packageFlags; + if (Build.VERSION.SDK_INT < 24) { + //noinspection deprecation + packageFlags = PackageManager.GET_UNINSTALLED_PACKAGES; + } else { + packageFlags = PackageManager.MATCH_UNINSTALLED_PACKAGES; + } ApplicationInfo info = packageManager.getApplicationInfo( Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, - PackageManager.GET_UNINSTALLED_PACKAGES); + packageFlags); if (0 == (info.flags & ApplicationInfo.FLAG_INSTALLED)) { // Need to reinstall the sample app showStatusProfile(); diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/SetupProfileFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/SetupProfileFragment.java index 29c36d410..28bd1dad7 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/SetupProfileFragment.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/SetupProfileFragment.java @@ -80,6 +80,7 @@ public class SetupProfileFragment extends Fragment implements View.OnClickListen intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, EnforcerDeviceAdminReceiver.getComponentName(activity)); } else { + //noinspection deprecation intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, activity.getApplicationContext().getPackageName()); intent.putExtra(EXTRA_DEVICE_ADMIN, EnforcerDeviceAdminReceiver.getComponentName(activity)); diff --git a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java index f4a4eb796..ed3258071 100644 --- a/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java +++ b/samples/browseable/AppRestrictionEnforcer/src/com.example.android.apprestrictionenforcer/StatusFragment.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.admin.DevicePolicyManager; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; @@ -85,9 +86,16 @@ public class StatusFragment extends Fragment implements View.OnClickListener { private void updateUi(Activity activity) { PackageManager packageManager = activity.getPackageManager(); try { + int packageFlags; + if (Build.VERSION.SDK_INT < 24) { + //noinspection deprecation + packageFlags = PackageManager.GET_UNINSTALLED_PACKAGES; + } else { + packageFlags = PackageManager.MATCH_UNINSTALLED_PACKAGES; + } ApplicationInfo info = packageManager.getApplicationInfo( Constants.PACKAGE_NAME_APP_RESTRICTION_SCHEMA, - PackageManager.GET_UNINSTALLED_PACKAGES); + packageFlags); DevicePolicyManager devicePolicyManager = (DevicePolicyManager) activity.getSystemService(Activity.DEVICE_POLICY_SERVICE); if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { diff --git a/samples/browseable/AppRestrictionSchema/AndroidManifest.xml b/samples/browseable/AppRestrictionSchema/AndroidManifest.xml index f8bbccc56..6ef22ede6 100644 --- a/samples/browseable/AppRestrictionSchema/AndroidManifest.xml +++ b/samples/browseable/AppRestrictionSchema/AndroidManifest.xml @@ -16,17 +16,17 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.example.android.apprestrictionschema" android:versionCode="1" android:versionName="1.0"> - <!-- uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" /--> - <application android:allowBackup="true" - android:icon="@drawable/ic_launcher" + android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + tools:ignore="GoogleAppIndexingWarning"> <meta-data android:name="android.content.APP_RESTRICTIONS" diff --git a/samples/browseable/AppRestrictionSchema/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 74344d7c3..000000000 --- a/samples/browseable/AppRestrictionSchema/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionSchema/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index a01dbd77b..000000000 --- a/samples/browseable/AppRestrictionSchema/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionSchema/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 19bb139de..000000000 --- a/samples/browseable/AppRestrictionSchema/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictionSchema/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 9922ae6e0..000000000 --- a/samples/browseable/AppRestrictionSchema/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null 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 01bda925d..de43e1a36 100644 --- a/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml +++ b/samples/browseable/AppRestrictionSchema/res/layout/fragment_app_restriction_schema.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2014 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,9 +16,9 @@ limitations under the License. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" @@ -32,50 +31,50 @@ limitations under the License. android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" - tools:text="@string/explanation_can_say_hello_true"/> + tools:text="@string/explanation_can_say_hello_true" /> <Button android:id="@+id/say_hello" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/margin_medium" android:layout_marginStart="@dimen/margin_medium" - android:text="@string/action_can_say_hello"/> - - <include layout="@layout/separator"/> + android:text="@string/action_can_say_hello" /> <TextView android:id="@+id/your_number" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/margin_medium" + android:layout_marginTop="@dimen/margin_medium" android:textAppearance="?android:attr/textAppearanceMedium" - tools:text="@string/your_number"/> - - <include layout="@layout/separator"/> + tools:text="@string/your_number" /> <TextView android:id="@+id/your_rank" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/margin_medium" + android:layout_marginTop="@dimen/margin_medium" android:textAppearance="?android:attr/textAppearanceMedium" - tools:text="@string/your_rank"/> - - <include layout="@layout/separator" android:id="@+id/bundle_separator"/> + tools:text="@string/your_rank" /> <TextView android:id="@+id/approvals_you_have" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/margin_medium" + android:layout_marginTop="@dimen/margin_medium" android:textAppearance="?android:attr/textAppearanceMedium" - tools:text="@string/approvals_you_have"/> - - <include layout="@layout/separator" android:id="@+id/bundle_array_separator" /> + tools:text="@string/approvals_you_have" /> <TextView android:id="@+id/your_items" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="@dimen/margin_medium" android:textAppearance="?android:attr/textAppearanceMedium" - tools:text="@string/your_items"/> + tools:text="@string/your_items" /> </LinearLayout> diff --git a/samples/browseable/AppRestrictionSchema/res/layout/separator.xml b/samples/browseable/AppRestrictionSchema/res/layout/separator.xml deleted file mode 100644 index 6927d801c..000000000 --- a/samples/browseable/AppRestrictionSchema/res/layout/separator.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2014 The Android Open Source Project - -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. ---> -<View xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="1dp" - android:layout_marginBottom="@dimen/margin_medium" - android:layout_marginTop="@dimen/margin_medium" - android:background="#9000"/> diff --git a/samples/browseable/AppRestrictionSchema/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..419e62861 --- /dev/null +++ b/samples/browseable/AppRestrictionSchema/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionSchema/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..16ec1ce20 --- /dev/null +++ b/samples/browseable/AppRestrictionSchema/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionSchema/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4a1929ee2 --- /dev/null +++ b/samples/browseable/AppRestrictionSchema/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionSchema/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..b35996420 --- /dev/null +++ b/samples/browseable/AppRestrictionSchema/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionSchema/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/AppRestrictionSchema/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..0c3cf753a --- /dev/null +++ b/samples/browseable/AppRestrictionSchema/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java b/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java index f15bd9d41..3f4cea012 100644 --- a/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java +++ b/samples/browseable/AppRestrictionSchema/src/com.example.android.apprestrictionschema/AppRestrictionSchemaFragment.java @@ -81,17 +81,11 @@ 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); - 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); - bundleArraySeparator.setVisibility(View.VISIBLE); mTextItems.setVisibility(View.VISIBLE); } else { - bundleSeparator.setVisibility(View.GONE); - bundleArraySeparator.setVisibility(View.GONE); mTextItems.setVisibility(View.GONE); } } diff --git a/samples/browseable/AppRestrictions/AndroidManifest.xml b/samples/browseable/AppRestrictions/AndroidManifest.xml index ddac9cfd1..5c0ca6489 100644 --- a/samples/browseable/AppRestrictions/AndroidManifest.xml +++ b/samples/browseable/AppRestrictions/AndroidManifest.xml @@ -18,6 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.example.android.apprestrictions" android:versionCode="1" android:versionName="1.0"> @@ -25,7 +26,9 @@ <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle --> <application android:label="@string/app_name" - android:icon="@drawable/ic_launcher"> + android:icon="@mipmap/ic_launcher" + android:allowBackup="true" + tools:ignore="GoogleAppIndexingWarning"> <activity android:name="MainActivity" android:label="@string/app_name" > diff --git a/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100755 index f36c473a1..000000000 --- a/samples/browseable/AppRestrictions/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100755 index 5ab2e0d33..000000000 --- a/samples/browseable/AppRestrictions/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100755 index 76228388e..000000000 --- a/samples/browseable/AppRestrictions/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100755 index 7f55feff2..000000000 --- a/samples/browseable/AppRestrictions/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/AppRestrictions/res/layout/main.xml b/samples/browseable/AppRestrictions/res/layout/main.xml index 55e2c8eb4..5228754f7 100644 --- a/samples/browseable/AppRestrictions/res/layout/main.xml +++ b/samples/browseable/AppRestrictions/res/layout/main.xml @@ -18,7 +18,7 @@ limitations under the License. android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:layout_margin="20dp"> <TextView android:layout_width="match_parent" @@ -35,7 +35,7 @@ limitations under the License. android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onCustomClicked"/> - <TextView android:layout_width="wrap_content" + <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:textSize="18sp" android:text="@string/custom_description" @@ -63,7 +63,7 @@ limitations under the License. <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="20dp"> + android:layout_marginStart="20dp"> <TextView android:layout_width="210dp" android:layout_height="wrap_content" android:textSize="18sp" @@ -80,7 +80,7 @@ limitations under the License. <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="20dp"> + android:layout_marginStart="20dp"> <TextView android:layout_width="210dp" android:layout_height="wrap_content" android:textSize="18sp" @@ -97,7 +97,7 @@ limitations under the License. <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="20dp"> + android:layout_marginStart="20dp"> <TextView android:layout_width="210dp" android:layout_height="wrap_content" android:textSize="18sp" diff --git a/samples/browseable/AppRestrictions/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f411ebbfd --- /dev/null +++ b/samples/browseable/AppRestrictions/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4489c8dd2 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..07473b8e1 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71d7bb727 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/AppRestrictions/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..8f9e6cc81 --- /dev/null +++ b/samples/browseable/AppRestrictions/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java index b04dfd1f7..516eb510f 100644 --- a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/CustomRestrictionsFragment.java @@ -29,6 +29,7 @@ import android.preference.Preference; import android.preference.PreferenceFragment; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -123,16 +124,14 @@ public class CustomRestrictionsFragment extends PreferenceFragment mChoicePref.setValue(entry.getSelectedString()); mChoiceEntry = entry; } else if (entry.getKey().equals(GetRestrictionsReceiver.KEY_MULTI_SELECT)) { - HashSet<String> set = new HashSet<String>(); - for (String value : entry.getAllSelectedStrings()) { - set.add(value); - } + HashSet<String> set = new HashSet<>(); + Collections.addAll(set, entry.getAllSelectedStrings()); mMultiPref.setValues(set); mMultiEntry = entry; } } } else { - mRestrictions = new ArrayList<RestrictionEntry>(); + mRestrictions = new ArrayList<>(); // Initializes the boolean restriction entry and updates its corresponding shared // preference value. @@ -155,13 +154,11 @@ public class CustomRestrictionsFragment extends PreferenceFragment GetRestrictionsReceiver.KEY_MULTI_SELECT)); mMultiEntry.setType(RestrictionEntry.TYPE_MULTI_SELECT); if (mMultiEntry.getAllSelectedStrings() != null) { - HashSet<String> set = new HashSet<String>(); + HashSet<String> set = new HashSet<>(); final String[] values = mRestrictionsBundle.getStringArray( GetRestrictionsReceiver.KEY_MULTI_SELECT); if (values != null) { - for (String value : values) { - set.add(value); - } + Collections.addAll(set, values); } mMultiPref.setValues(set); } @@ -173,7 +170,7 @@ public class CustomRestrictionsFragment extends PreferenceFragment // activity finishes. Intent intent = new Intent(getActivity().getIntent()); intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST, - new ArrayList<RestrictionEntry>(mRestrictions)); + new ArrayList<>(mRestrictions)); getActivity().setResult(Activity.RESULT_OK, intent); } @@ -183,9 +180,12 @@ public class CustomRestrictionsFragment extends PreferenceFragment mBooleanEntry.setSelectedState((Boolean) newValue); } else if (preference == mChoicePref) { mChoiceEntry.setSelectedString((String) newValue); - } else if (preference == mMultiPref) { - String[] selectedStrings = new String[((Set<String>)newValue).size()]; + } else if (preference == mMultiPref && newValue instanceof Set) { + // newValue is a Set<String>, skip the lint warning. + //noinspection unchecked + String[] selectedStrings = new String[((Set<String>) newValue).size()]; int i = 0; + //noinspection unchecked for (String value : (Set<String>) newValue) { selectedStrings[i++] = value; } @@ -195,7 +195,7 @@ public class CustomRestrictionsFragment extends PreferenceFragment // Saves all the app restriction configuration changes from the custom activity. Intent intent = new Intent(getActivity().getIntent()); intent.putParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST, - new ArrayList<RestrictionEntry>(mRestrictions)); + new ArrayList<>(mRestrictions)); getActivity().setResult(Activity.RESULT_OK, intent); return true; } diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java index bb5a28391..a17a6c118 100644 --- a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/GetRestrictionsReceiver.java @@ -88,7 +88,7 @@ public class GetRestrictionsReceiver extends BroadcastReceiver { // Demonstrates the creation of standard app restriction types: boolean, single choice, and // multi-select. private ArrayList<RestrictionEntry> initRestrictions(Context context) { - ArrayList<RestrictionEntry> newRestrictions = new ArrayList<RestrictionEntry>(); + ArrayList<RestrictionEntry> newRestrictions = new ArrayList<>(); Resources res = context.getResources(); RestrictionEntry reBoolean = new RestrictionEntry(KEY_BOOLEAN, false); diff --git a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java index 57c443906..1e76fbda8 100644 --- a/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java +++ b/samples/browseable/AppRestrictions/src/com.example.android.apprestrictions/MainActivity.java @@ -120,12 +120,10 @@ public class MainActivity extends Activity { * * This flag is used by {@code GetRestrictionsReceiver} to determine if a custom app * restriction activity should be used. - * - * @param view */ public void onCustomClicked(View view) { final SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); - editor.putBoolean(CUSTOM_CONFIG_KEY, mCustomConfig.isChecked()).commit(); + editor.putBoolean(CUSTOM_CONFIG_KEY, mCustomConfig.isChecked()).apply(); } } diff --git a/samples/browseable/AppShortcuts/AndroidManifest.xml b/samples/browseable/AppShortcuts/AndroidManifest.xml index 9dfc7a3a8..183bc84d9 100644 --- a/samples/browseable/AppShortcuts/AndroidManifest.xml +++ b/samples/browseable/AppShortcuts/AndroidManifest.xml @@ -23,7 +23,8 @@ <application android:label="@string/app_name" - android:icon="@drawable/app" + android:icon="@mipmap/ic_launcher" + android:roundIcon="@mipmap/ic_launcher_round" android:resizeableActivity="true"> <activity android:name="com.example.android.appshortcuts.Main"> diff --git a/samples/browseable/AppShortcuts/res/drawable-nodpi/app.png b/samples/browseable/AppShortcuts/res/drawable-nodpi/app.png Binary files differdeleted file mode 100644 index 39ca2f973..000000000 --- a/samples/browseable/AppShortcuts/res/drawable-nodpi/app.png +++ /dev/null diff --git a/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..cb86b9743 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher_round.png b/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..7502f5d60 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-hdpi/ic_launcher_round.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..bfeec7a38 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher_round.png b/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..13ca6d75f --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-mdpi/ic_launcher_round.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..358442e05 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher_round.png b/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..ff93f4d14 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..c99a1a000 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..8b642db83 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..3bcda818b --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..a49362e32 --- /dev/null +++ b/samples/browseable/AppShortcuts/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/samples/browseable/AsymmetricFingerprintDialog/AndroidManifest.xml b/samples/browseable/AsymmetricFingerprintDialog/AndroidManifest.xml index d1cf9f870..4dce26600 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/AndroidManifest.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/AndroidManifest.xml @@ -14,25 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.asymmetricfingerprintdialog" - android:versionCode="1" - android:versionName="1.0"> +<manifest package="com.example.android.asymmetricfingerprintdialog" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:versionCode="1" + android:versionName="1.0"> - <uses-permission android:name="android.permission.USE_FINGERPRINT"/> + <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <application - android:name=".InjectedApplication" - android:allowBackup="true" - android:label="@string/app_name" - android:icon="@mipmap/ic_launcher" - android:theme="@style/AppTheme"> + android:name=".InjectedApplication" + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/AppTheme" + tools:ignore="GoogleAppIndexingWarning"> - <activity android:name=".MainActivity" - android:label="@string/app_name"> + <activity + android:name=".MainActivity" + android:label="@string/app_name"> <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/samples/browseable/AsymmetricFingerprintDialog/res/layout/activity_main.xml b/samples/browseable/AsymmetricFingerprintDialog/res/layout/activity_main.xml index 8f30557b9..16b29bd89 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/res/layout/activity_main.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/res/layout/activity_main.xml @@ -31,7 +31,8 @@ android:layout_marginBottom="32dp" android:layout_gravity="center_horizontal" android:scaleType="fitCenter" - android:src="@drawable/android_robot"/> + android:src="@drawable/android_robot" + android:contentDescription="@string/description_bugdroid_icon" /> <LinearLayout android:layout_width="match_parent" @@ -77,8 +78,7 @@ android:layout_gravity="end" android:textColor="?android:attr/textColorPrimaryInverse" android:text="@string/purchase" - android:id="@+id/purchase_button" - android:layout_alignParentEnd="true"/> + android:id="@+id/purchase_button"/> <TextView android:id="@+id/confirmation_message" diff --git a/samples/browseable/AsymmetricFingerprintDialog/res/layout/fingerprint_dialog_content.xml b/samples/browseable/AsymmetricFingerprintDialog/res/layout/fingerprint_dialog_content.xml index 3929ebae6..5bb65b2ac 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/res/layout/fingerprint_dialog_content.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/res/layout/fingerprint_dialog_content.xml @@ -41,7 +41,8 @@ android:layout_alignParentStart="true" android:layout_below="@+id/fingerprint_description" android:layout_marginTop="20dp" - android:src="@drawable/ic_fp_40px" /> + android:src="@drawable/ic_fp_40px" + android:contentDescription="@string/description_fingerprint_icon"/> <TextView android:id="@+id/fingerprint_status" diff --git a/samples/browseable/AsymmetricFingerprintDialog/res/menu/menu_main.xml b/samples/browseable/AsymmetricFingerprintDialog/res/menu/menu_main.xml index 73f5e89a0..15969703a 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/res/menu/menu_main.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/res/menu/menu_main.xml @@ -15,7 +15,12 @@ ~ limitations under the License --> <menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> - <item android:id="@+id/action_settings" android:title="@string/action_settings" - android:orderInCategory="100" android:showAsAction="never" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".MainActivity"> + <item + android:id="@+id/action_settings" + android:orderInCategory="100" + android:title="@string/action_settings" + app:showAsAction="never" /> </menu> diff --git a/samples/browseable/AsymmetricFingerprintDialog/res/values/strings.xml b/samples/browseable/AsymmetricFingerprintDialog/res/values/strings.xml index f44c06d68..86b8d422d 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/res/values/strings.xml +++ b/samples/browseable/AsymmetricFingerprintDialog/res/values/strings.xml @@ -19,7 +19,7 @@ <string name="cancel">Cancel</string> <string name="use_password">Use password</string> <string name="sign_in">Sign in</string> - <string name="ok">Ok</string> + <string name="ok">OK</string> <string name="password">Password</string> <string name="fingerprint_description">Confirm fingerprint to continue</string> <string name="fingerprint_hint">Touch sensor</string> @@ -36,4 +36,6 @@ <string name="use_fingerprint_in_future">Use fingerprint in the future</string> <string name="use_fingerprint_to_authenticate_title">Use fingerprint to authenticate</string> <string name="use_fingerprint_to_authenticate_key" >use_fingerprint_to_authenticate_key</string> + <string name="description_bugdroid_icon">Android bugdroid image</string> + <string name="description_fingerprint_icon">Fingerprint icon</string> </resources> diff --git a/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java b/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java index a56556f13..fbe8ec9c3 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java +++ b/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java @@ -19,8 +19,8 @@ package com.example.android.asymmetricfingerprintdialog; import com.example.android.asymmetricfingerprintdialog.server.StoreBackend; import com.example.android.asymmetricfingerprintdialog.server.Transaction; -import android.app.Activity; import android.app.DialogFragment; +import android.content.Context; import android.content.SharedPreferences; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; @@ -158,9 +158,9 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mActivity = (MainActivity) activity; + public void onAttach(Context context) { + super.onAttach(context); + mActivity = (MainActivity) getActivity(); } /** diff --git a/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintUiHelper.java b/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintUiHelper.java index f65481161..dee427099 100644 --- a/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintUiHelper.java +++ b/samples/browseable/AsymmetricFingerprintDialog/src/com.example.android.asymmetricfingerprintdialog/FingerprintUiHelper.java @@ -27,7 +27,11 @@ import javax.inject.Inject; /** * Small helper class to manage text/icon around fingerprint authentication UI. + * This class assumes that the {@link android.Manifest.permission#USE_FINGERPRINT} + * permission has already been granted. (As of API 23 this permission is normal instead of dangerous + * and is granted at install time.) */ +@SuppressWarnings("MissingPermission") public class FingerprintUiHelper extends FingerprintManager.AuthenticationCallback { @VisibleForTesting static final long ERROR_TIMEOUT_MILLIS = 1600; diff --git a/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml b/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml index 1c3b255fa..1f8a43198 100644 --- a/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml +++ b/samples/browseable/BasicAndroidKeyStore/AndroidManifest.xml @@ -26,7 +26,7 @@ <application android:allowBackup="true" android:label="@string/app_name" - android:icon="@drawable/ic_launcher" + android:icon="@mipmap/ic_launcher" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index b1efaf4b2..000000000 --- a/samples/browseable/BasicAndroidKeyStore/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index f5f9244f2..000000000 --- a/samples/browseable/BasicAndroidKeyStore/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 5d07b3f06..000000000 --- a/samples/browseable/BasicAndroidKeyStore/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 6ef21e1f4..000000000 --- a/samples/browseable/BasicAndroidKeyStore/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/samples/browseable/BasicAndroidKeyStore/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c57b83ab7 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c43fc24ac --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..4255f237f --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..f6ca8a92b --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..0f623f6f4 --- /dev/null +++ b/samples/browseable/BasicAndroidKeyStore/res/mipmap-xxxhdpi/ic_launcher.png 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 e6244bfb6..3616e88e4 100644 --- a/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java +++ b/samples/browseable/BasicAndroidKeyStore/src/com.example.android.basicandroidkeystore/BasicAndroidKeyStoreFragment.java @@ -16,15 +16,18 @@ package com.example.android.basicandroidkeystore; +import com.example.android.common.logger.Log; + import android.content.Context; +import android.os.Build; import android.os.Bundle; import android.security.KeyPairGeneratorSpec; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; import android.support.v4.app.Fragment; import android.util.Base64; import android.view.MenuItem; -import com.example.android.common.logger.Log; - import java.io.IOException; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; @@ -39,6 +42,7 @@ import java.security.Signature; import java.security.SignatureException; import java.security.UnrecoverableEntryException; import java.security.cert.CertificateException; +import java.security.spec.AlgorithmParameterSpec; import java.util.Calendar; import java.util.GregorianCalendar; @@ -46,7 +50,7 @@ import javax.security.auth.x500.X500Principal; public class BasicAndroidKeyStoreFragment extends Fragment { - public static final String TAG = "BasicAndroidKeyStoreFragment"; + public static final String TAG = "KeyStoreFragment"; // BEGIN_INCLUDE(values) @@ -159,36 +163,54 @@ public class BasicAndroidKeyStoreFragment extends Fragment { end.add(Calendar.YEAR, 1); //END_INCLUDE(create_valid_dates) - - // BEGIN_INCLUDE(create_spec) - // The KeyPairGeneratorSpec object is how parameters for your key pair are passed - // to the KeyPairGenerator. For a fun home game, count how many classes in this sample - // start with the phrase "KeyPair". - KeyPairGeneratorSpec spec = - new KeyPairGeneratorSpec.Builder(context) - // You'll use the alias later to retrieve the key. It's a key for the key! - .setAlias(mAlias) - // The subject used for the self-signed certificate of the generated pair - .setSubject(new X500Principal("CN=" + mAlias)) - // The serial number used for the self-signed certificate of the - // generated pair. - .setSerialNumber(BigInteger.valueOf(1337)) - // Date range of validity for the generated pair. - .setStartDate(start.getTime()) - .setEndDate(end.getTime()) - .build(); - // END_INCLUDE(create_spec) - // BEGIN_INCLUDE(create_keypair) // Initialize a KeyPair generator using the the intended algorithm (in this example, RSA // and the KeyStore. This example uses the AndroidKeyStore. KeyPairGenerator kpGenerator = KeyPairGenerator .getInstance(SecurityConstants.TYPE_RSA, SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE); + // END_INCLUDE(create_keypair) + + // BEGIN_INCLUDE(create_spec) + // The KeyPairGeneratorSpec object is how parameters for your key pair are passed + // to the KeyPairGenerator. + AlgorithmParameterSpec spec; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // Below Android M, use the KeyPairGeneratorSpec.Builder. + + spec = new KeyPairGeneratorSpec.Builder(context) + // You'll use the alias later to retrieve the key. It's a key for the key! + .setAlias(mAlias) + // The subject used for the self-signed certificate of the generated pair + .setSubject(new X500Principal("CN=" + mAlias)) + // The serial number used for the self-signed certificate of the + // generated pair. + .setSerialNumber(BigInteger.valueOf(1337)) + // Date range of validity for the generated pair. + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + + + } else { + // On Android M or above, use the KeyGenparameterSpec.Builder and specify permitted + // properties and restrictions of the key. + spec = new KeyGenParameterSpec.Builder(mAlias, KeyProperties.PURPOSE_SIGN) + .setCertificateSubject(new X500Principal("CN=" + mAlias)) + .setDigests(KeyProperties.DIGEST_SHA256) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .setCertificateSerialNumber(BigInteger.valueOf(1337)) + .setCertificateNotBefore(start.getTime()) + .setCertificateNotAfter(end.getTime()) + .build(); + } + kpGenerator.initialize(spec); + KeyPair kp = kpGenerator.generateKeyPair(); + // END_INCLUDE(create_spec) Log.d(TAG, "Public Key is: " + kp.getPublic().toString()); - // END_INCLUDE(create_keypair) } /** diff --git a/samples/browseable/BasicGestureDetect/res/menu/main.xml b/samples/browseable/BasicGestureDetect/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/BasicGestureDetect/res/menu/main.xml +++ b/samples/browseable/BasicGestureDetect/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/BasicImmersiveMode/res/menu/main.xml b/samples/browseable/BasicImmersiveMode/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/BasicImmersiveMode/res/menu/main.xml +++ b/samples/browseable/BasicImmersiveMode/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatFragment.java b/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatFragment.java index 8ee906246..b77ef558e 100644 --- a/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatFragment.java +++ b/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatFragment.java @@ -188,7 +188,7 @@ public class BluetoothChatFragment extends Fragment { } /** - * Makes this device discoverable. + * Makes this device discoverable for 300 seconds (5 minutes). */ private void ensureDiscoverable() { if (mBluetoothAdapter.getScanMode() != @@ -355,7 +355,7 @@ public class BluetoothChatFragment extends Fragment { } /** - * Establish connection with other divice + * Establish connection with other device * * @param data An {@link Intent} with {@link DeviceListActivity#EXTRA_DEVICE_ADDRESS} extra. * @param secure Socket Security type - Secure (true) , Insecure (false) diff --git a/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatService.java b/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatService.java index a1e7cc016..ad4a6d1bb 100644 --- a/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatService.java +++ b/samples/browseable/BluetoothChat/src/com.example.android.bluetoothchat/BluetoothChatService.java @@ -60,6 +60,7 @@ public class BluetoothChatService { private ConnectThread mConnectThread; private ConnectedThread mConnectedThread; private int mState; + private int mNewState; // Constants that indicate the current connection state public static final int STATE_NONE = 0; // we're doing nothing @@ -76,20 +77,20 @@ public class BluetoothChatService { public BluetoothChatService(Context context, Handler handler) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mState = STATE_NONE; + mNewState = mState; mHandler = handler; } /** - * Set the current state of the chat connection - * - * @param state An integer defining the current connection state + * Update UI title according to the current state of the chat connection */ - private synchronized void setState(int state) { - Log.d(TAG, "setState() " + mState + " -> " + state); - mState = state; + private synchronized void updateUserInterfaceTitle() { + mState = getState(); + Log.d(TAG, "updateUserInterfaceTitle() " + mNewState + " -> " + mState); + mNewState = mState; // Give the new state to the Handler so the UI Activity can update - mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget(); + mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, mNewState, -1).sendToTarget(); } /** @@ -118,8 +119,6 @@ public class BluetoothChatService { mConnectedThread = null; } - setState(STATE_LISTEN); - // Start the thread to listen on a BluetoothServerSocket if (mSecureAcceptThread == null) { mSecureAcceptThread = new AcceptThread(true); @@ -129,6 +128,8 @@ public class BluetoothChatService { mInsecureAcceptThread = new AcceptThread(false); mInsecureAcceptThread.start(); } + // Update UI title + updateUserInterfaceTitle(); } /** @@ -157,7 +158,8 @@ public class BluetoothChatService { // Start the thread to connect with the given device mConnectThread = new ConnectThread(device, secure); mConnectThread.start(); - setState(STATE_CONNECTING); + // Update UI title + updateUserInterfaceTitle(); } /** @@ -202,8 +204,8 @@ public class BluetoothChatService { bundle.putString(Constants.DEVICE_NAME, device.getName()); msg.setData(bundle); mHandler.sendMessage(msg); - - setState(STATE_CONNECTED); + // Update UI title + updateUserInterfaceTitle(); } /** @@ -231,7 +233,9 @@ public class BluetoothChatService { mInsecureAcceptThread.cancel(); mInsecureAcceptThread = null; } - setState(STATE_NONE); + mState = STATE_NONE; + // Update UI title + updateUserInterfaceTitle(); } /** @@ -263,6 +267,10 @@ public class BluetoothChatService { msg.setData(bundle); mHandler.sendMessage(msg); + mState = STATE_NONE; + // Update UI title + updateUserInterfaceTitle(); + // Start the service over to restart listening mode BluetoothChatService.this.start(); } @@ -278,6 +286,10 @@ public class BluetoothChatService { msg.setData(bundle); mHandler.sendMessage(msg); + mState = STATE_NONE; + // Update UI title + updateUserInterfaceTitle(); + // Start the service over to restart listening mode BluetoothChatService.this.start(); } @@ -309,6 +321,7 @@ public class BluetoothChatService { Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e); } mmServerSocket = tmp; + mState = STATE_LISTEN; } public void run() { @@ -396,6 +409,7 @@ public class BluetoothChatService { Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e); } mmSocket = tmp; + mState = STATE_CONNECTING; } public void run() { @@ -465,6 +479,7 @@ public class BluetoothChatService { mmInStream = tmpIn; mmOutStream = tmpOut; + mState = STATE_CONNECTED; } public void run() { @@ -484,8 +499,6 @@ public class BluetoothChatService { } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); - // Start the service over to restart listening mode - BluetoothChatService.this.start(); break; } } diff --git a/samples/browseable/CardView/_index.jd b/samples/browseable/CardView/_index.jd index 3d8c9e072..c4dd1db16 100644 --- a/samples/browseable/CardView/_index.jd +++ b/samples/browseable/CardView/_index.jd @@ -5,7 +5,7 @@ sample.group=UI <p> - This sample demonstrates how to use CardView introduced in the support library for the - Android L preview. + This sample demonstrates how to use CardView introduced in the support library in + Android 5.0. </p> diff --git a/samples/browseable/CardView/res/values/base-strings.xml b/samples/browseable/CardView/res/values/base-strings.xml index 12863226d..4db434965 100644 --- a/samples/browseable/CardView/res/values/base-strings.xml +++ b/samples/browseable/CardView/res/values/base-strings.xml @@ -21,8 +21,8 @@ <![CDATA[ - This sample demonstrates how to use CardView introduced in the support library for the - Android L preview. + This sample demonstrates how to use CardView introduced in the support library in + Android 5.0. ]]> diff --git a/samples/browseable/CommitContentSampleApp/AndroidManifest.xml b/samples/browseable/CommitContentSampleApp/AndroidManifest.xml index 16c93b117..a7335943b 100644 --- a/samples/browseable/CommitContentSampleApp/AndroidManifest.xml +++ b/samples/browseable/CommitContentSampleApp/AndroidManifest.xml @@ -4,6 +4,7 @@ <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" + android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher.png Binary files differindex cde69bccc..e8a0aafc2 100644..100755 --- a/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..f2618b01e --- /dev/null +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-hdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher.png Binary files differindex c133a0cbd..f4ac0f7d6 100644..100755 --- a/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..68363185b --- /dev/null +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-mdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher.png Binary files differindex bfa42f0e7..74ee41ade 100644..100755 --- a/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..22be9b823 --- /dev/null +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher.png Binary files differindex 324e72cdd..c7eaeb6d0 100644..100755 --- a/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..d701598e6 --- /dev/null +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher.png Binary files differindex aee44e138..dd7ed2e2c 100644..100755 --- a/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..b4b3ca7bc --- /dev/null +++ b/samples/browseable/CommitContentSampleApp/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/AndroidManifest.xml b/samples/browseable/CommitContentSampleIME/AndroidManifest.xml index f9891ffcb..86a7b37bc 100644 --- a/samples/browseable/CommitContentSampleIME/AndroidManifest.xml +++ b/samples/browseable/CommitContentSampleIME/AndroidManifest.xml @@ -4,6 +4,7 @@ <application android:allowBackup="false" android:icon="@mipmap/ic_launcher" + android:roundIcon="@mipmap/ic_launcher_round" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher.png Binary files differindex cde69bccc..e8a0aafc2 100644..100755 --- a/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..f2618b01e --- /dev/null +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-hdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher.png Binary files differindex c133a0cbd..f4ac0f7d6 100644..100755 --- a/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..68363185b --- /dev/null +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-mdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher.png Binary files differindex bfa42f0e7..74ee41ade 100644..100755 --- a/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..22be9b823 --- /dev/null +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher.png Binary files differindex 324e72cdd..c7eaeb6d0 100644..100755 --- a/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..d701598e6 --- /dev/null +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher.png Binary files differindex aee44e138..dd7ed2e2c 100644..100755 --- a/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher.png +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000..b4b3ca7bc --- /dev/null +++ b/samples/browseable/CommitContentSampleIME/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/samples/browseable/CommitContentSampleIME/src/com.example.android.commitcontent.ime/ImageKeyboard.java b/samples/browseable/CommitContentSampleIME/src/com.example.android.commitcontent.ime/ImageKeyboard.java index eb5dae7af..920910087 100644 --- a/samples/browseable/CommitContentSampleIME/src/com.example.android.commitcontent.ime/ImageKeyboard.java +++ b/samples/browseable/CommitContentSampleIME/src/com.example.android.commitcontent.ime/ImageKeyboard.java @@ -49,7 +49,7 @@ import java.io.OutputStream; public class ImageKeyboard extends InputMethodService { private static final String TAG = "ImageKeyboard"; - private static final String AUTHORITY = "com.example.android.supportv13.sampleime.inputcontent"; + private static final String AUTHORITY = "com.example.android.commitcontent.ime.inputcontent"; private static final String MIME_TYPE_GIF = "image/gif"; private static final String MIME_TYPE_PNG = "image/png"; private static final String MIME_TYPE_WEBP = "image/webp"; diff --git a/samples/browseable/DataLayer/Wearable/AndroidManifest.xml b/samples/browseable/DataLayer/Wearable/AndroidManifest.xml index 6608bb705..2bc5f4c7d 100644 --- a/samples/browseable/DataLayer/Wearable/AndroidManifest.xml +++ b/samples/browseable/DataLayer/Wearable/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.datalayer" > <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -29,6 +29,10 @@ android:theme="@android:style/Theme.DeviceDefault"> <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + + <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> diff --git a/samples/browseable/DirectBoot/src/com.example.android.directboot/SchedulerFragment.java b/samples/browseable/DirectBoot/src/com.example.android.directboot/SchedulerFragment.java index 0f32c2589..008961f8a 100644 --- a/samples/browseable/DirectBoot/src/com.example.android.directboot/SchedulerFragment.java +++ b/samples/browseable/DirectBoot/src/com.example.android.directboot/SchedulerFragment.java @@ -129,7 +129,7 @@ public class SchedulerFragment extends Fragment { @Override public void onReceive(Context context, Intent intent) { - Alarm alarm = intent.getParcelableExtra(AlarmIntentService.ALARM_KEY); + Alarm alarm = AlarmUtil.readAlarm(intent.getExtras()); mAlarmAdapter.deleteAlarm(alarm); } } diff --git a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/Alarm.java b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/Alarm.java index 82712c6b7..27ca5c453 100644 --- a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/Alarm.java +++ b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/Alarm.java @@ -16,66 +16,44 @@ package com.example.android.directboot.alarms; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.NonNull; - import org.json.JSONException; import org.json.JSONObject; +import android.support.annotation.NonNull; + import java.util.Calendar; import java.util.Objects; /** * Class represents a single alarm. */ -public class Alarm implements Comparable<Alarm>, Parcelable { +public class Alarm implements Comparable<Alarm> { public int id; + public int month; + public int date; + /** Integer as a 24-hour format */ public int hour; - public int minute; - - public Alarm() {} - protected Alarm(Parcel in) { - id = in.readInt(); - month = in.readInt(); - date = in.readInt(); - hour = in.readInt(); - minute = in.readInt(); - } - - public static final Creator<Alarm> CREATOR = new Creator<Alarm>() { - @Override - public Alarm createFromParcel(Parcel in) { - return new Alarm(in); - } - - @Override - public Alarm[] newArray(int size) { - return new Alarm[size]; - } - }; + public int minute; - @Override - public int describeContents() { - return 0; + public Alarm(int id, int month, int date, int hour, int minute) { + this.id = id; + this.month = month; + this.date = date; + this.hour = hour; + this.minute = minute; } - @Override - public void writeToParcel(Parcel parcel, int i) { - parcel.writeInt(id); - parcel.writeInt(month); - parcel.writeInt(date); - parcel.writeInt(hour); - parcel.writeInt(minute); + public Alarm() { } /** * Serialize the instance as a JSON String. + * * @return serialized JSON String. */ public String toJson() { diff --git a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmIntentService.java b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmIntentService.java index 446345cd4..3909482ef 100644 --- a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmIntentService.java +++ b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmIntentService.java @@ -34,7 +34,17 @@ public class AlarmIntentService extends IntentService { public static final String ALARM_WENT_OFF_ACTION = AlarmIntentService.class.getName() + ".ALARM_WENT_OFF"; - public static final String ALARM_KEY = "alarm_instance"; + + + public static final String KEY_ALARM_ID = "alarm_id"; + + public static final String KEY_ALARM_MONTH = "alarm_month"; + + public static final String KEY_ALARM_DATE = "alarm_date"; + + public static final String KEY_ALARM_HOUR = "alarm_hour"; + + public static final String KEY_ALARM_MINUTE = "alarm_minute"; public AlarmIntentService() { super(AlarmIntentService.class.getName()); @@ -43,7 +53,7 @@ public class AlarmIntentService extends IntentService { @Override protected void onHandleIntent(Intent intent) { Context context = getApplicationContext(); - Alarm alarm = intent.getParcelableExtra(ALARM_KEY); + Alarm alarm = AlarmUtil.readAlarm(intent.getExtras()); NotificationManager notificationManager = context .getSystemService(NotificationManager.class); @@ -59,7 +69,7 @@ public class AlarmIntentService extends IntentService { AlarmStorage alarmStorage = new AlarmStorage(context); alarmStorage.deleteAlarm(alarm); Intent wentOffIntent = new Intent(ALARM_WENT_OFF_ACTION); - wentOffIntent.putExtra(ALARM_KEY, alarm); + wentOffIntent.putExtras(AlarmUtil.writeAlarm(alarm)); LocalBroadcastManager.getInstance(context).sendBroadcast(wentOffIntent); } } diff --git a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmUtil.java b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmUtil.java index b80e8c9d7..29c01c99d 100644 --- a/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmUtil.java +++ b/samples/browseable/DirectBoot/src/com.example.android.directboot/alarms/AlarmUtil.java @@ -20,6 +20,7 @@ import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.util.Log; import java.util.Calendar; @@ -45,7 +46,9 @@ public class AlarmUtil { */ public void scheduleAlarm(Alarm alarm) { Intent intent = new Intent(mContext, AlarmIntentService.class); - intent.putExtra(AlarmIntentService.ALARM_KEY, alarm); + Bundle extras = writeAlarm(alarm); + intent.putExtras(extras); + PendingIntent pendingIntent = PendingIntent .getService(mContext, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT); Calendar alarmTime = Calendar.getInstance(); @@ -71,7 +74,6 @@ public class AlarmUtil { */ public void cancelAlarm(Alarm alarm) { Intent intent = new Intent(mContext, AlarmIntentService.class); - intent.putExtra(AlarmIntentService.ALARM_KEY, alarm); PendingIntent pendingIntent = PendingIntent .getService(mContext, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT); mAlarmManager.cancel(pendingIntent); @@ -94,4 +96,25 @@ public class AlarmUtil { } return alarmTime; } + + public static Alarm readAlarm(Bundle extras) { + int id = extras.getInt(AlarmIntentService.KEY_ALARM_ID); + int month = extras.getInt(AlarmIntentService.KEY_ALARM_MONTH); + int date = extras.getInt(AlarmIntentService.KEY_ALARM_DATE); + int hour = extras.getInt(AlarmIntentService.KEY_ALARM_HOUR); + int minute = extras.getInt(AlarmIntentService.KEY_ALARM_MINUTE); + + return new Alarm(id, month, date, hour, minute); + } + + public static Bundle writeAlarm(Alarm alarm){ + Bundle extras = new Bundle(); + extras.putInt(AlarmIntentService.KEY_ALARM_ID, alarm.id); + extras.putInt(AlarmIntentService.KEY_ALARM_MONTH, alarm.month); + extras.putInt(AlarmIntentService.KEY_ALARM_DATE, alarm.date); + extras.putInt(AlarmIntentService.KEY_ALARM_HOUR, alarm.hour); + extras.putInt(AlarmIntentService.KEY_ALARM_MINUTE, alarm.minute); + + return extras; + } } diff --git a/samples/browseable/ElevationBasic/res/layout/elevation_basic.xml b/samples/browseable/ElevationBasic/res/layout/elevation_basic.xml index 83ab1d282..d6c951066 100644 --- a/samples/browseable/ElevationBasic/res/layout/elevation_basic.xml +++ b/samples/browseable/ElevationBasic/res/layout/elevation_basic.xml @@ -16,14 +16,13 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/floating_shape" android:layout_width="80dp" android:layout_height="80dp" - android:layout_marginRight="40dp" + android:layout_marginEnd="40dp" android:background="@drawable/shape" android:elevation="30dp" android:layout_gravity="center"/> @@ -31,7 +30,7 @@ android:id="@+id/floating_shape_2" android:layout_width="80dp" android:layout_height="80dp" - android:layout_marginLeft="25dp" + android:layout_marginStart="25dp" android:background="@drawable/shape2" android:layout_gravity="center"/> </FrameLayout> diff --git a/samples/browseable/ElevationBasic/src/com.example.android.elevationbasic/ElevationBasicFragment.java b/samples/browseable/ElevationBasic/src/com.example.android.elevationbasic/ElevationBasicFragment.java index 71edea5b3..4a005907f 100644 --- a/samples/browseable/ElevationBasic/src/com.example.android.elevationbasic/ElevationBasicFragment.java +++ b/samples/browseable/ElevationBasic/src/com.example.android.elevationbasic/ElevationBasicFragment.java @@ -19,7 +19,6 @@ package com.example.android.elevationbasic; import android.support.v4.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; -import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; diff --git a/samples/browseable/FingerprintDialog/AndroidManifest.xml b/samples/browseable/FingerprintDialog/AndroidManifest.xml index 77d994977..337f64199 100644 --- a/samples/browseable/FingerprintDialog/AndroidManifest.xml +++ b/samples/browseable/FingerprintDialog/AndroidManifest.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- +<?xml version="1.0" encoding="UTF-8"?><!-- Copyright 2015 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,24 +13,28 @@ See the License for the specific language governing permissions and limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.fingerprintdialog" - android:versionCode="1" - android:versionName="1.0"> +<manifest package="com.example.android.fingerprintdialog" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:versionCode="1" + android:versionName="1.0"> - <uses-permission android:name="android.permission.USE_FINGERPRINT"/> + <uses-permission android:name="android.permission.USE_FINGERPRINT" /> <application - android:allowBackup="true" - android:label="@string/application_name" - android:icon="@mipmap/ic_launcher" - android:theme="@style/Theme.AppCompat.Light"> + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/application_name" + android:supportsRtl="true" + android:theme="@style/Theme.AppCompat.Light" + tools:ignore="GoogleAppIndexingWarning"> - <activity android:name=".MainActivity" - android:label="@string/application_name"> + <activity + android:name=".MainActivity" + android:label="@string/application_name"> <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> diff --git a/samples/browseable/FingerprintDialog/res/layout/activity_main.xml b/samples/browseable/FingerprintDialog/res/layout/activity_main.xml index 075899f8f..f0a596fcd 100644 --- a/samples/browseable/FingerprintDialog/res/layout/activity_main.xml +++ b/samples/browseable/FingerprintDialog/res/layout/activity_main.xml @@ -31,7 +31,8 @@ android:layout_marginBottom="32dp" android:layout_gravity="center_horizontal" android:scaleType="fitCenter" - android:src="@drawable/android_robot"/> + android:src="@drawable/android_robot" + android:contentDescription="@string/description_bugdroid_icon"/> <LinearLayout android:layout_width="match_parent" diff --git a/samples/browseable/FingerprintDialog/res/layout/fingerprint_dialog_content.xml b/samples/browseable/FingerprintDialog/res/layout/fingerprint_dialog_content.xml index 3929ebae6..5bb65b2ac 100644 --- a/samples/browseable/FingerprintDialog/res/layout/fingerprint_dialog_content.xml +++ b/samples/browseable/FingerprintDialog/res/layout/fingerprint_dialog_content.xml @@ -41,7 +41,8 @@ android:layout_alignParentStart="true" android:layout_below="@+id/fingerprint_description" android:layout_marginTop="20dp" - android:src="@drawable/ic_fp_40px" /> + android:src="@drawable/ic_fp_40px" + android:contentDescription="@string/description_fingerprint_icon"/> <TextView android:id="@+id/fingerprint_status" diff --git a/samples/browseable/FingerprintDialog/res/menu/menu_main.xml b/samples/browseable/FingerprintDialog/res/menu/menu_main.xml index 73f5e89a0..6a6ba0f99 100644 --- a/samples/browseable/FingerprintDialog/res/menu/menu_main.xml +++ b/samples/browseable/FingerprintDialog/res/menu/menu_main.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (C) 2015 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,7 +14,12 @@ ~ limitations under the License --> <menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> - <item android:id="@+id/action_settings" android:title="@string/action_settings" - android:orderInCategory="100" android:showAsAction="never" /> + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".MainActivity"> + <item + android:id="@+id/action_settings" + android:orderInCategory="100" + android:title="@string/action_settings" + app:showAsAction="never" /> </menu> diff --git a/samples/browseable/FingerprintDialog/res/values/strings.xml b/samples/browseable/FingerprintDialog/res/values/strings.xml index 86f200c97..b08560601 100644 --- a/samples/browseable/FingerprintDialog/res/values/strings.xml +++ b/samples/browseable/FingerprintDialog/res/values/strings.xml @@ -25,7 +25,7 @@ <string name="cancel">Cancel</string> <string name="use_password">Use password</string> <string name="sign_in">Sign in</string> - <string name="ok">Ok</string> + <string name="ok">OK</string> <string name="password">Password</string> <string name="fingerprint_description">Confirm fingerprint to continue</string> <string name="fingerprint_hint">Touch sensor</string> @@ -45,4 +45,6 @@ <string name="use_fingerprint_in_future">Use fingerprint in the future</string> <string name="use_fingerprint_to_authenticate_title">Use fingerprint to authenticate</string> <string name="use_fingerprint_to_authenticate_key" >use_fingerprint_to_authenticate_key</string> + <string name="description_bugdroid_icon">Android bugdroid image</string> + <string name="description_fingerprint_icon">Fingerprint icon</string> </resources> diff --git a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java index 053757330..9f4a68417 100644 --- a/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java +++ b/samples/browseable/FingerprintDialog/src/com.example.android.fingerprintdialog/FingerprintAuthenticationDialogFragment.java @@ -16,8 +16,8 @@ package com.example.android.fingerprintdialog; -import android.app.Activity; import android.app.DialogFragment; +import android.content.Context; import android.content.SharedPreferences; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; @@ -134,11 +134,11 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment } @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mActivity = (MainActivity) activity; - mInputMethodManager = mActivity.getSystemService(InputMethodManager.class); - mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(mActivity); + public void onAttach(Context context) { + super.onAttach(context); + mActivity = (MainActivity) getActivity(); + mInputMethodManager = context.getSystemService(InputMethodManager.class); + mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); } /** diff --git a/samples/browseable/GridViewPager/AndroidManifest.xml b/samples/browseable/GridViewPager/AndroidManifest.xml index e25cd6373..bd6df9452 100644 --- a/samples/browseable/GridViewPager/AndroidManifest.xml +++ b/samples/browseable/GridViewPager/AndroidManifest.xml @@ -27,6 +27,11 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" > + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <activity android:name="com.example.android.wearable.gridviewpager.MainActivity" android:label="@string/app_name" > diff --git a/samples/browseable/ImmersiveMode/res/menu/main.xml b/samples/browseable/ImmersiveMode/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/ImmersiveMode/res/menu/main.xml +++ b/samples/browseable/ImmersiveMode/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/JobScheduler/AndroidManifest.xml b/samples/browseable/JobScheduler/AndroidManifest.xml index 06a927b94..8ef81ba4f 100644 --- a/samples/browseable/JobScheduler/AndroidManifest.xml +++ b/samples/browseable/JobScheduler/AndroidManifest.xml @@ -38,7 +38,7 @@ </activity> <service - android:name=".service.TestJobService" + android:name=".service.MyJobService" android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/> </application> diff --git a/samples/browseable/JobScheduler/res/layout/sample_main.xml b/samples/browseable/JobScheduler/res/layout/sample_main.xml index 94b16240d..a11983be8 100644 --- a/samples/browseable/JobScheduler/res/layout/sample_main.xml +++ b/samples/browseable/JobScheduler/res/layout/sample_main.xml @@ -21,143 +21,204 @@ limitations under the License. <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_weight="1" + android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="100dp"> + <TextView android:id="@+id/onstart_textview" android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_margin="12dp" android:layout_weight="1" android:background="@color/none_received" android:gravity="center" - android:text="@string/onstarttask"/> + android:text="@string/onstarttask" /> + <TextView android:id="@+id/onstop_textview" android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_margin="12dp" android:layout_weight="1" android:background="@color/none_received" android:gravity="center" - android:text="@string/onstoptask"/> + android:text="@string/onstoptask" /> + </LinearLayout> - <Button - android:id="@+id/finished_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="20dp" - android:layout_marginBottom="5dp" - android:onClick="finishJob" - android:text="@string/finish_job_button_text"/> <TextView android:id="@+id/task_params" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/defaultparamtext" + android:layout_marginBottom="10dp" + android:layout_weight="1" android:gravity="center" - android:textSize="20dp" android:padding="15dp" - android:layout_marginBottom="10dp" /> + android:textSize="20sp" /> - <TextView + <Button + android:id="@+id/finished_button" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/constraints" - android:layout_margin="15dp" - android:textSize="18dp"/> - <LinearLayout + android:padding="20dp" + android:layout_marginBottom="5dp" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:onClick="finishJob" + android:text="@string/finish_job_button_text"/> + + <TableLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginLeft="10dp"> - <LinearLayout + android:layout_height="match_parent" + android:layout_margin="8dp"> + + <TableRow android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="match_parent"> + <TextView - android:layout_width="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginRight="12dp" + android:gravity="right|center_vertical" + android:text="@string/work_duration" + android:textSize="14sp" /> + + <EditText + android:id="@+id/duration_time" + android:layout_width="48dp" android:layout_height="wrap_content" + android:inputType="number" + android:text="2" /> + + </TableRow> + + <TableRow + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginRight="12dp" + android:gravity="right|center_vertical" android:text="@string/connectivity" - android:layout_marginRight="10dp"/> + android:textSize="14sp" /> + <RadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> - <RadioButton android:id="@+id/checkbox_any" + + <RadioButton + android:id="@+id/checkbox_any" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/any"/> - <RadioButton android:id="@+id/checkbox_unmetered" + android:checked="true" + android:text="@string/any" /> + + <RadioButton + android:id="@+id/checkbox_unmetered" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/unmetered"/> + android:text="@string/unmetered" /> </RadioGroup> - </LinearLayout> - <LinearLayout + </TableRow> + + <TableRow android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/timing"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="15dp" - android:textSize="17dp" - android:text="@string/delay"/> - <EditText - android:id="@+id/delay_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> + android:layout_height="match_parent"> + <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/deadline" - android:textSize="17dp"/> - <EditText - android:id="@+id/deadline_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> - </LinearLayout> - <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginRight="12dp" + android:gravity="right|center_vertical" + android:text="@string/delay" + android:textSize="14sp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timing" /> + + <EditText + android:id="@+id/delay_time" + android:layout_width="48dp" + android:layout_height="wrap_content" + android:inputType="number" + android:text="0" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/deadline" + android:textSize="14sp" /> + + <EditText + android:id="@+id/deadline_time" + android:layout_width="48dp" + android:layout_height="wrap_content" + android:inputType="number" + android:text="15" /> + + </LinearLayout> + + </TableRow> + + <TableRow android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="match_parent" + android:layout_weight="1"> + <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginRight="12dp" + android:gravity="right|center_vertical" android:text="@string/charging_caption" - android:layout_marginRight="15dp"/> + android:textSize="14sp" /> + <CheckBox + android:id="@+id/checkbox_charging" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/checkbox_charging" - android:text="@string/charging_text"/> - </LinearLayout> - <LinearLayout + android:text="@string/charging_text" /> + + </TableRow> + + <TableRow android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="match_parent" + android:layout_weight="1"> + <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/idle_caption" - android:layout_marginRight="15dp"/> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginRight="12dp" + android:gravity="right|center_vertical" + android:text="@string/idle_caption" /> + <CheckBox + android:id="@+id/checkbox_idle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/checkbox_idle" - android:text="@string/idle_mode_text"/> - </LinearLayout> + android:text="@string/idle_mode_text" /> + </TableRow> + + </TableLayout> - </LinearLayout> <Button android:id="@+id/schedule_button" android:layout_width="match_parent" diff --git a/samples/browseable/JobScheduler/res/values/strings.xml b/samples/browseable/JobScheduler/res/values/strings.xml index 89524b25a..0d551eba8 100644 --- a/samples/browseable/JobScheduler/res/values/strings.xml +++ b/samples/browseable/JobScheduler/res/values/strings.xml @@ -21,16 +21,19 @@ limitations under the License. <string name="defaultparamtext">task params will show up here.</string> <string name="schedule_job_button_text">Schedule Job</string> <string name="cancel_all_jobs_button_text">Cancel all</string> - <string name="finish_job_button_text">taskFinished</string> + <string name="finish_job_button_text">Finish last task</string> <string name="idle_mode_text">Requires device in idle mode.</string> <string name="charging_caption">Charging:</string> <string name="charging_text">Requires device plugged in.</string> <string name="idle_caption">Idle:</string> - <string name="constraints">Constraints</string> <string name="connectivity">Connectivity:</string> <string name="any">Any</string> <string name="unmetered">WiFi</string> <string name="timing">Timing:</string> <string name="delay">Delay:</string> <string name="deadline">Deadline:</string> + <string name="work_duration">Work duration:</string> + <string name="cancelled_job">Cancelled job %d</string> + <string name="no_jobs_to_cancel">No jobs to cancel</string> + <string name="all_jobs_cancelled">All jobs cancelled</string> </resources> diff --git a/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/MainActivity.java b/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/MainActivity.java index 624e22d02..fbb817261 100644 --- a/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/MainActivity.java +++ b/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/MainActivity.java @@ -18,17 +18,19 @@ package com.example.android.jobscheduler; import android.app.Activity; import android.app.job.JobInfo; -import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; +import android.os.PersistableBundle; +import android.support.annotation.ColorRes; +import android.support.annotation.Nullable; import android.text.TextUtils; +import android.util.Log; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; @@ -36,105 +38,97 @@ import android.widget.RadioButton; import android.widget.TextView; import android.widget.Toast; -import com.example.android.jobscheduler.service.TestJobService; +import com.example.android.jobscheduler.service.MyJobService; +import java.lang.ref.WeakReference; +import java.util.List; + + +/** + * Schedules and configures jobs to be executed by a {@link JobScheduler}. + * <p> + * {@link MyJobService} can send messages to this via a {@link Messenger} + * that is sent in the Intent that starts the Service. + */ public class MainActivity extends Activity { - private static final String TAG = "MainActivity"; + private static final String TAG = MainActivity.class.getSimpleName(); - public static final int MSG_UNCOLOUR_START = 0; - public static final int MSG_UNCOLOUR_STOP = 1; - public static final int MSG_SERVICE_OBJ = 2; + public static final int MSG_UNCOLOR_START = 0; + public static final int MSG_UNCOLOR_STOP = 1; + public static final int MSG_COLOR_START = 2; + public static final int MSG_COLOR_STOP = 3; + + public static final String MESSENGER_INTENT_KEY + = BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY"; + public static final String WORK_DURATION_KEY = + BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY"; + + private EditText mDelayEditText; + private EditText mDeadlineEditText; + private EditText mDurationTimeEditText; + private RadioButton mWiFiConnectivityRadioButton; + private RadioButton mAnyConnectivityRadioButton; + private CheckBox mRequiresChargingCheckBox; + private CheckBox mRequiresIdleCheckbox; + + private ComponentName mServiceComponent; + + private int mJobId = 0; + + // Handler for incoming messages from the service. + private IncomingMessageHandler mHandler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sample_main); - Resources res = getResources(); - defaultColor = res.getColor(R.color.none_received); - startJobColor = res.getColor(R.color.start_received); - stopJobColor = res.getColor(R.color.stop_received); // Set up UI. - mShowStartView = (TextView) findViewById(R.id.onstart_textview); - mShowStopView = (TextView) findViewById(R.id.onstop_textview); - mParamsTextView = (TextView) findViewById(R.id.task_params); mDelayEditText = (EditText) findViewById(R.id.delay_time); + mDurationTimeEditText = (EditText) findViewById(R.id.duration_time); mDeadlineEditText = (EditText) findViewById(R.id.deadline_time); mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered); mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any); mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging); mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle); - mServiceComponent = new ComponentName(this, TestJobService.class); - // Start service and provide it a way to communicate with us. - Intent startServiceIntent = new Intent(this, TestJobService.class); - startServiceIntent.putExtra("messenger", new Messenger(mHandler)); - startService(startServiceIntent); - } - // UI fields. - int defaultColor; - int startJobColor; - int stopJobColor; - - private TextView mShowStartView; - private TextView mShowStopView; - private TextView mParamsTextView; - private EditText mDelayEditText; - private EditText mDeadlineEditText; - private RadioButton mWiFiConnectivityRadioButton; - private RadioButton mAnyConnectivityRadioButton; - private CheckBox mRequiresChargingCheckBox; - private CheckBox mRequiresIdleCheckbox; + mServiceComponent = new ComponentName(this, MyJobService.class); - ComponentName mServiceComponent; - /** Service object to interact scheduled jobs. */ - TestJobService mTestService; - - private static int kJobId = 0; + mHandler = new IncomingMessageHandler(this); + } - Handler mHandler = new Handler(/* default looper */) { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UNCOLOUR_START: - mShowStartView.setBackgroundColor(defaultColor); - break; - case MSG_UNCOLOUR_STOP: - mShowStopView.setBackgroundColor(defaultColor); - break; - case MSG_SERVICE_OBJ: - mTestService = (TestJobService) msg.obj; - mTestService.setUiCallback(MainActivity.this); - } - } - }; + @Override + protected void onStop() { + // A service can be "started" and/or "bound". In this case, it's "started" by this Activity + // and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call + // to stopService() won't prevent scheduled jobs to be processed. However, failing + // to call stopService() would keep it alive indefinitely. + stopService(new Intent(this, MyJobService.class)); + super.onStop(); + } - private boolean ensureTestService() { - if (mTestService == null) { - Toast.makeText(MainActivity.this, "Service null, never got callback?", - Toast.LENGTH_SHORT).show(); - return false; - } - return true; + @Override + protected void onStart() { + super.onStart(); + // Start service and provide it a way to communicate with this class. + Intent startServiceIntent = new Intent(this, MyJobService.class); + Messenger messengerIncoming = new Messenger(mHandler); + startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming); + startService(startServiceIntent); } /** - * UI onclick listener to schedule a job. What this job is is defined in - * TestJobService#scheduleJob(). + * Executed when user clicks on SCHEDULE JOB. */ public void scheduleJob(View v) { - if (!ensureTestService()) { - return; - } - - JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent); + JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent); String delay = mDelayEditText.getText().toString(); - if (delay != null && !TextUtils.isEmpty(delay)) { + if (!TextUtils.isEmpty(delay)) { builder.setMinimumLatency(Long.valueOf(delay) * 1000); } String deadline = mDeadlineEditText.getText().toString(); - if (deadline != null && !TextUtils.isEmpty(deadline)) { + if (!TextUtils.isEmpty(deadline)) { builder.setOverrideDeadline(Long.valueOf(deadline) * 1000); } boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked(); @@ -147,49 +141,128 @@ public class MainActivity extends Activity { builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked()); builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked()); - mTestService.scheduleJob(builder.build()); + // Extras, work duration. + PersistableBundle extras = new PersistableBundle(); + String workDuration = mDurationTimeEditText.getText().toString(); + if (TextUtils.isEmpty(workDuration)) { + workDuration = "1"; + } + extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000); + + builder.setExtras(extras); + // Schedule job + Log.d(TAG, "Scheduling job"); + JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + tm.schedule(builder.build()); } + /** + * Executed when user clicks on CANCEL ALL. + */ public void cancelAllJobs(View v) { - JobScheduler tm = - (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); tm.cancelAll(); + Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show(); } /** - * UI onclick listener to call jobFinished() in our service. + * Executed when user clicks on FINISH LAST TASK. */ public void finishJob(View v) { - if (!ensureTestService()) { - return; + JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs(); + if (allPendingJobs.size() > 0) { + // Finish the last one + int jobId = allPendingJobs.get(0).getId(); + jobScheduler.cancel(jobId); + Toast.makeText( + MainActivity.this, String.format(getString(R.string.cancelled_job), jobId), + Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText( + MainActivity.this, getString(R.string.no_jobs_to_cancel), + Toast.LENGTH_SHORT).show(); } - mTestService.callJobFinished(); - mParamsTextView.setText(""); } /** - * Receives callback from the service when a job has landed - * on the app. Colours the UI and post a message to - * uncolour it after a second. + * A {@link Handler} allows you to send messages associated with a thread. A {@link Messenger} + * uses this handler to communicate from {@link MyJobService}. It's also used to make + * the start and stop views blink for a short period of time. */ - public void onReceivedStartJob(JobParameters params) { - mShowStartView.setBackgroundColor(startJobColor); - Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START); - mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second. - mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras()); - } + private static class IncomingMessageHandler extends Handler { - /** - * Receives callback from the service when a job that - * previously landed on the app must stop executing. - * Colours the UI and post a message to uncolour it after a - * second. - */ - public void onReceivedStopJob() { - mShowStopView.setBackgroundColor(stopJobColor); - Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP); - mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second. - mParamsTextView.setText(""); + // Prevent possible leaks with a weak reference. + private WeakReference<MainActivity> mActivity; + + IncomingMessageHandler(MainActivity activity) { + super(/* default looper */); + this.mActivity = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + MainActivity mainActivity = mActivity.get(); + if (mainActivity == null) { + // Activity is no longer available, exit. + return; + } + View showStartView = mainActivity.findViewById(R.id.onstart_textview); + View showStopView = mainActivity.findViewById(R.id.onstop_textview); + Message m; + switch (msg.what) { + /* + * Receives callback from the service when a job has landed + * on the app. Turns on indicator and sends a message to turn it off after + * a second. + */ + case MSG_COLOR_START: + // Start received, turn on the indicator and show text. + showStartView.setBackgroundColor(getColor(R.color.start_received)); + updateParamsTextView(msg.obj, "started"); + + // Send message to turn it off after a second. + m = Message.obtain(this, MSG_UNCOLOR_START); + sendMessageDelayed(m, 1000L); + break; + /* + * Receives callback from the service when a job that previously landed on the + * app must stop executing. Turns on indicator and sends a message to turn it + * off after two seconds. + */ + case MSG_COLOR_STOP: + // Stop received, turn on the indicator and show text. + showStopView.setBackgroundColor(getColor(R.color.stop_received)); + updateParamsTextView(msg.obj, "stopped"); + + // Send message to turn it off after a second. + m = obtainMessage(MSG_UNCOLOR_STOP); + sendMessageDelayed(m, 2000L); + break; + case MSG_UNCOLOR_START: + showStartView.setBackgroundColor(getColor(R.color.none_received)); + updateParamsTextView(null, ""); + break; + case MSG_UNCOLOR_STOP: + showStopView.setBackgroundColor(getColor(R.color.none_received)); + updateParamsTextView(null, ""); + break; + } + } + + private void updateParamsTextView(@Nullable Object jobId, String action) { + TextView paramsTextView = (TextView) mActivity.get().findViewById(R.id.task_params); + if (jobId == null) { + paramsTextView.setText(""); + return; + } + String jobIdText = String.valueOf(jobId); + paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action)); + } + + private int getColor(@ColorRes int color) { + return mActivity.get().getResources().getColor(color); + } } } diff --git a/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/MyJobService.java b/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/MyJobService.java new file mode 100644 index 000000000..2f521bb0d --- /dev/null +++ b/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/MyJobService.java @@ -0,0 +1,119 @@ +/* + * Copyright 2014 Google Inc. + * + * 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.example.android.jobscheduler.service; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.annotation.Nullable; +import android.util.Log; + + +import static com.example.android.jobscheduler.MainActivity.MESSENGER_INTENT_KEY; +import static com.example.android.jobscheduler.MainActivity.MSG_COLOR_START; +import static com.example.android.jobscheduler.MainActivity.MSG_COLOR_STOP; +import static com.example.android.jobscheduler.MainActivity.WORK_DURATION_KEY; + + +/** + * Service to handle callbacks from the JobScheduler. Requests scheduled with the JobScheduler + * ultimately land on this service's "onStartJob" method. It runs jobs for a specific amount of time + * and finishes them. It keeps the activity updated with changes via a Messenger. + */ +public class MyJobService extends JobService { + + private static final String TAG = MyJobService.class.getSimpleName(); + + private Messenger mActivityMessenger; + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "Service created"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "Service destroyed"); + } + + /** + * When the app's MainActivity is created, it starts this service. This is so that the + * activity and this service can communicate back and forth. See "setUiCallback()" + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY); + return START_NOT_STICKY; + } + + @Override + public boolean onStartJob(final JobParameters params) { + // The work that this service "does" is simply wait for a certain duration and finish + // the job (on another thread). + + sendMessage(MSG_COLOR_START, params.getJobId()); + + long duration = params.getExtras().getLong(WORK_DURATION_KEY); + + // Uses a handler to delay the execution of jobFinished(). + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + sendMessage(MSG_COLOR_STOP, params.getJobId()); + jobFinished(params, false); + } + }, duration); + Log.i(TAG, "on start job: " + params.getJobId()); + + // Return true as there's more work to be done with this job. + return true; + } + + @Override + public boolean onStopJob(JobParameters params) { + // Stop tracking these job parameters, as we've 'finished' executing. + sendMessage(MSG_COLOR_STOP, params.getJobId()); + Log.i(TAG, "on stop job: " + params.getJobId()); + + // Return false to drop the job. + return false; + } + + private void sendMessage(int messageID, @Nullable Object params) { + // If this service is launched by the JobScheduler, there's no callback Messenger. It + // only exists when the MainActivity calls startService() with the callback in the Intent. + if (mActivityMessenger == null) { + Log.d(TAG, "Service is bound, not started. There's no callback to send a message to."); + return; + } + Message m = Message.obtain(); + m.what = messageID; + m.obj = params; + try { + mActivityMessenger.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Error passing service object back to activity."); + } + } +} diff --git a/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/TestJobService.java b/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/TestJobService.java deleted file mode 100644 index 8608be570..000000000 --- a/samples/browseable/JobScheduler/src/com.example.android.jobscheduler/service/TestJobService.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * 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.example.android.jobscheduler.service; - -import android.app.job.JobInfo; -import android.app.job.JobScheduler; -import android.app.job.JobParameters; -import android.app.job.JobService; -import android.content.Context; -import android.content.Intent; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.util.Log; - -import com.example.android.jobscheduler.MainActivity; - -import java.util.LinkedList; - - -/** - * Service to handle callbacks from the JobScheduler. Requests scheduled with the JobScheduler - * ultimately land on this service's "onStartJob" method. Currently all this does is post a message - * to the app's main activity to change the state of the UI. - */ -public class TestJobService extends JobService { - private static final String TAG = "SyncService"; - - @Override - public void onCreate() { - super.onCreate(); - Log.i(TAG, "Service created"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.i(TAG, "Service destroyed"); - } - - /** - * When the app's MainActivity is created, it starts this service. This is so that the - * activity and this service can communicate back and forth. See "setUiCalback()" - */ - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Messenger callback = intent.getParcelableExtra("messenger"); - Message m = Message.obtain(); - m.what = MainActivity.MSG_SERVICE_OBJ; - m.obj = this; - try { - callback.send(m); - } catch (RemoteException e) { - Log.e(TAG, "Error passing service object back to activity."); - } - return START_NOT_STICKY; - } - - @Override - public boolean onStartJob(JobParameters params) { - // We don't do any real 'work' in this sample app. All we'll - // do is track which jobs have landed on our service, and - // update the UI accordingly. - jobParamsMap.add(params); - if (mActivity != null) { - mActivity.onReceivedStartJob(params); - } - Log.i(TAG, "on start job: " + params.getJobId()); - return true; - } - - @Override - public boolean onStopJob(JobParameters params) { - // Stop tracking these job parameters, as we've 'finished' executing. - jobParamsMap.remove(params); - if (mActivity != null) { - mActivity.onReceivedStopJob(); - } - Log.i(TAG, "on stop job: " + params.getJobId()); - return true; - } - - MainActivity mActivity; - private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>(); - - public void setUiCallback(MainActivity activity) { - mActivity = activity; - } - - /** Send job to the JobScheduler. */ - public void scheduleJob(JobInfo t) { - Log.d(TAG, "Scheduling job"); - JobScheduler tm = - (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); - tm.schedule(t); - } - - /** - * Not currently used, but as an exercise you can hook this - * up to a button in the UI to finish a job that has landed - * in onStartJob(). - */ - public boolean callJobFinished() { - JobParameters params = jobParamsMap.poll(); - if (params == null) { - return false; - } else { - jobFinished(params, false); - return true; - } - } - -} diff --git a/samples/browseable/JumpingJack/AndroidManifest.xml b/samples/browseable/JumpingJack/AndroidManifest.xml index f6cf22047..0e0475a27 100644 --- a/samples/browseable/JumpingJack/AndroidManifest.xml +++ b/samples/browseable/JumpingJack/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.jumpingjack"> <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="22" /> + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -29,6 +29,11 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <activity android:name=".MainActivity" android:label="@string/app_name"> diff --git a/samples/browseable/LNotifications/res/layout/activity_notification.xml b/samples/browseable/LNotifications/res/layout/activity_notification.xml index cd0cd6878..8723923fd 100644 --- a/samples/browseable/LNotifications/res/layout/activity_notification.xml +++ b/samples/browseable/LNotifications/res/layout/activity_notification.xml @@ -14,14 +14,26 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.example.android.lnotifications.LNotificationActivity"> -</FrameLayout> + + <android.support.design.widget.TabLayout + android:id="@+id/tabs" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:tabMode="fixed" + app:tabGravity="fill" /> + + <android.support.v4.view.ViewPager + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="match_parent" > + </android.support.v4.view.ViewPager> +</LinearLayout> diff --git a/samples/browseable/LNotifications/src/com.example.android.lnotifications/HeadsUpNotificationFragment.java b/samples/browseable/LNotifications/src/com.example.android.lnotifications/HeadsUpNotificationFragment.java index d304cf4ae..89904031a 100644 --- a/samples/browseable/LNotifications/src/com.example.android.lnotifications/HeadsUpNotificationFragment.java +++ b/samples/browseable/LNotifications/src/com.example.android.lnotifications/HeadsUpNotificationFragment.java @@ -16,13 +16,13 @@ package com.example.android.lnotifications; -import android.app.Fragment; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -110,8 +110,7 @@ public class HeadsUpNotificationFragment extends Fragment { * * @return A Notification instance. */ - //@VisibleForTesting - Notification createNotification(boolean makeHeadsUpNotification) { + private Notification createNotification(boolean makeHeadsUpNotification) { Notification.Builder notificationBuilder = new Notification.Builder(getActivity()) .setSmallIcon(R.drawable.ic_launcher_notification) .setPriority(Notification.PRIORITY_DEFAULT) diff --git a/samples/browseable/LNotifications/src/com.example.android.lnotifications/LNotificationActivity.java b/samples/browseable/LNotifications/src/com.example.android.lnotifications/LNotificationActivity.java index fbc3e6232..8b44ed327 100644 --- a/samples/browseable/LNotifications/src/com.example.android.lnotifications/LNotificationActivity.java +++ b/samples/browseable/LNotifications/src/com.example.android.lnotifications/LNotificationActivity.java @@ -16,65 +16,74 @@ package com.example.android.lnotifications; -import android.app.ActionBar; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentTransaction; import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; + +import static com.example.android.lnotifications.R.id.pager; /** * Launcher Activity for the L Notification samples application. */ -public class LNotificationActivity extends Activity { +public class LNotificationActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification); - setTitle(R.string.title_lnotification_activity); - ActionBar actionBar = getActionBar(); - // Use ViewPager in the support library where possible. - // At this time, the support library for L is not ready so using the deprecated method - // to create tabs. - actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - ActionBar.Tab tabHeadsUpNotification = actionBar.newTab().setText("Heads Up"); - ActionBar.Tab tabVisibilityMetadata = actionBar.newTab().setText("Visibility"); - ActionBar.Tab tabOtherMetadata = actionBar.newTab().setText("Others"); - tabHeadsUpNotification.setTabListener(new FragmentTabListener(HeadsUpNotificationFragment - .newInstance())); - tabVisibilityMetadata.setTabListener(new FragmentTabListener(VisibilityMetadataFragment - .newInstance())); - tabOtherMetadata.setTabListener(new FragmentTabListener(OtherMetadataFragment.newInstance - ())); - actionBar.addTab(tabHeadsUpNotification, 0); - actionBar.addTab(tabVisibilityMetadata, 1); - actionBar.addTab(tabOtherMetadata, 2); + // Show 3 tabs with the different notification options. + ViewPager viewPager = (ViewPager) findViewById(pager); + TabLayout tabs = (TabLayout) findViewById(R.id.tabs); + + NotificationsPagerAdapter pagerAdapter = + new NotificationsPagerAdapter(getSupportFragmentManager()); + viewPager.setAdapter(pagerAdapter); + tabs.setupWithViewPager(viewPager); } - /** - * TabListener that replaces a Fragment when a tab is clicked. - */ - private static class FragmentTabListener implements ActionBar.TabListener { - public Fragment fragment; + private static class NotificationsPagerAdapter extends FragmentPagerAdapter { - public FragmentTabListener(Fragment fragment) { - this.fragment = fragment; + NotificationsPagerAdapter(FragmentManager fm) { + super(fm); } @Override - public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { - //do nothing. + public Fragment getItem(int position) { + switch (position) { + case 0: + return HeadsUpNotificationFragment.newInstance(); + case 1: + return VisibilityMetadataFragment.newInstance(); + case 2: + return OtherMetadataFragment.newInstance(); + default: + break; + } + return null; } @Override - public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { - ft.replace(R.id.container, fragment); + public int getCount() { + return 3; } @Override - public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { - ft.remove(fragment); + public CharSequence getPageTitle(int position) { + switch (position) { + case 0: + return "Heads Up"; + case 1: + return "Visibility"; + case 2: + return "Others"; + default: + return super.getPageTitle(position); + } } } } diff --git a/samples/browseable/LNotifications/src/com.example.android.lnotifications/OtherMetadataFragment.java b/samples/browseable/LNotifications/src/com.example.android.lnotifications/OtherMetadataFragment.java index 51616e700..0da2c2ade 100644 --- a/samples/browseable/LNotifications/src/com.example.android.lnotifications/OtherMetadataFragment.java +++ b/samples/browseable/LNotifications/src/com.example.android.lnotifications/OtherMetadataFragment.java @@ -17,7 +17,6 @@ package com.example.android.lnotifications; import android.app.Activity; -import android.app.Fragment; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; @@ -30,6 +29,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.provider.MediaStore; +import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -42,7 +42,6 @@ import android.widget.TextView; import android.widget.Toast; import java.io.IOException; -import java.util.Random; /** * Fragment that demonstrates how to attach metadata introduced in Android L, such as @@ -58,10 +57,10 @@ public class OtherMetadataFragment extends Fragment { public static final int REQUEST_CODE_PICK_CONTACT = 1; /** - * Incremental Integer used for ID for notifications so that each notification will be + * Incremental int used for ID for notifications so that each notification will be * treated differently. */ - private Integer mIncrementalNotificationId = Integer.valueOf(0); + private int mIncrementalNotificationId = 0; private NotificationManager mNotificationManager; @@ -85,8 +84,7 @@ public class OtherMetadataFragment extends Fragment { /** * Holds a URI for the person to be attached to the notification. */ - //@VisibleForTesting - Uri mContactUri; + private Uri mContactUri; /** * Use this factory method to create a new instance of @@ -177,8 +175,7 @@ public class OtherMetadataFragment extends Fragment { * * @return A Notification instance. */ - //@VisibleForTesting - Notification createNotification(Priority priority, Category category, Uri contactUri) { + private Notification createNotification(Priority priority, Category category, Uri contactUri) { Notification.Builder notificationBuilder = new Notification.Builder(getActivity()) .setContentTitle("Notification with other metadata") .setSmallIcon(R.drawable.ic_launcher_notification) @@ -207,7 +204,7 @@ public class OtherMetadataFragment extends Fragment { private void showNotificationClicked(Priority priority, Category category, Uri contactUri) { // Assigns a unique (incremented) notification ID in order to treat each notification as a // different one. This helps demonstrate how a priority flag affects ordering. - mIncrementalNotificationId = new Integer(mIncrementalNotificationId + 1); + mIncrementalNotificationId++; mNotificationManager.notify(mIncrementalNotificationId, createNotification(priority, category, contactUri)); Toast.makeText(getActivity(), "Show Notification clicked", Toast.LENGTH_SHORT).show(); @@ -295,8 +292,7 @@ public class OtherMetadataFragment extends Fragment { * Enum indicating possible categories in {@link Notification} used from * {@link #mCategorySpinner}. */ - //@VisibleForTesting - static enum Category { + private enum Category { ALARM("alarm"), CALL("call"), EMAIL("email"), @@ -328,8 +324,7 @@ public class OtherMetadataFragment extends Fragment { * Enum indicating possible priorities in {@link Notification} used from * {@link #mPrioritySpinner}. */ - //@VisibleForTesting - static enum Priority { + private enum Priority { DEFAULT(Notification.PRIORITY_DEFAULT), MAX(Notification.PRIORITY_MAX), HIGH(Notification.PRIORITY_HIGH), diff --git a/samples/browseable/LNotifications/src/com.example.android.lnotifications/VisibilityMetadataFragment.java b/samples/browseable/LNotifications/src/com.example.android.lnotifications/VisibilityMetadataFragment.java index 616632b34..c12d1082e 100644 --- a/samples/browseable/LNotifications/src/com.example.android.lnotifications/VisibilityMetadataFragment.java +++ b/samples/browseable/LNotifications/src/com.example.android.lnotifications/VisibilityMetadataFragment.java @@ -16,11 +16,11 @@ package com.example.android.lnotifications; -import android.app.Fragment; import android.app.Notification; import android.app.NotificationManager; import android.content.Context; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,8 +28,6 @@ import android.widget.Button; import android.widget.RadioGroup; import android.widget.Toast; -import java.util.Random; - /** * Fragment that demonstrates how notifications with different visibility metadata differ on @@ -45,10 +43,10 @@ public class VisibilityMetadataFragment extends Fragment { private RadioGroup mRadioGroup; /** - * Incremental Integer used for ID for notifications so that each notification will be + * Incremental int used for ID for notifications so that each notification will be * treated differently. */ - private Integer mIncrementalNotificationId = Integer.valueOf(0); + private int mIncrementalNotificationId = 0; /** * Button to show a notification. @@ -106,8 +104,7 @@ public class VisibilityMetadataFragment extends Fragment { * * @return A Notification instance. */ - //@VisibleForTesting - Notification createNotification(NotificationVisibility visibility) { + private Notification createNotification(NotificationVisibility visibility) { Notification.Builder notificationBuilder = new Notification.Builder(getActivity()) .setContentTitle("Notification for Visibility metadata"); @@ -150,7 +147,7 @@ public class VisibilityMetadataFragment extends Fragment { // Assigns a unique (incremented) notification ID in order to treat each notification as a // different one. This helps demonstrate how a notification with a different visibility // level differs on the lockscreen. - mIncrementalNotificationId = new Integer(mIncrementalNotificationId + 1); + mIncrementalNotificationId++; mNotificationManager.notify(mIncrementalNotificationId, createNotification(visibility)); Toast.makeText(getActivity(), "Show Notification clicked", Toast.LENGTH_SHORT).show(); } @@ -160,8 +157,7 @@ public class VisibilityMetadataFragment extends Fragment { * representation of visibility levels, an icon ID to create a notification) to * create a notification. */ - //@VisibleForTesting - static enum NotificationVisibility { + private enum NotificationVisibility { PUBLIC(Notification.VISIBILITY_PUBLIC, "Public", R.drawable.ic_public_notification), PRIVATE(Notification.VISIBILITY_PRIVATE, "Private", R.drawable.ic_private_notification), SECRET(Notification.VISIBILITY_SECRET, "Secret", R.drawable.ic_secret_notification); diff --git a/samples/browseable/NetworkConnect/AndroidManifest.xml b/samples/browseable/NetworkConnect/AndroidManifest.xml index 00ce7f3d0..654203b61 100644 --- a/samples/browseable/NetworkConnect/AndroidManifest.xml +++ b/samples/browseable/NetworkConnect/AndroidManifest.xml @@ -31,8 +31,8 @@ android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/Theme.Sample" - android:allowBackup="true"> - + android:allowBackup="true" + android:networkSecurityConfig="@xml/network_security_config"> <activity android:name="com.example.android.networkconnect.MainActivity" android:label="@string/app_name" diff --git a/samples/browseable/NetworkConnect/_index.jd b/samples/browseable/NetworkConnect/_index.jd index 7be3ce205..a816b6286 100644 --- a/samples/browseable/NetworkConnect/_index.jd +++ b/samples/browseable/NetworkConnect/_index.jd @@ -6,6 +6,6 @@ sample.group=Connectivity <p> This sample demonstrates how to connect to the network and fetch raw HTML using - HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. + HttpsURLConnection. AsyncTask is used to perform the fetch on a background thread. </p> diff --git a/samples/browseable/NetworkConnect/res/layout/sample_main.xml b/samples/browseable/NetworkConnect/res/layout/sample_main.xml index 76fa7d783..00e219c5b 100755 --- a/samples/browseable/NetworkConnect/res/layout/sample_main.xml +++ b/samples/browseable/NetworkConnect/res/layout/sample_main.xml @@ -21,20 +21,22 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> - <fragment - android:name="com.example.android.networkconnect.SimpleTextFragment" - android:id="@+id/intro_fragment" + <TextView + android:id="@+id/intro_text" android:layout_weight="1" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:textSize="16sp" + android:text="@string/welcome_message" + android:freezesText="true"/> <View android:layout_width="fill_parent" android:layout_height="1dp" android:background="@android:color/darker_gray"/> - <fragment - android:name="com.example.android.common.logger.LogFragment" - android:id="@+id/log_fragment" + <TextView + android:id="@+id/data_text" android:layout_weight="1" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:freezesText="true"/> </LinearLayout> diff --git a/samples/browseable/NetworkConnect/res/values/base-strings.xml b/samples/browseable/NetworkConnect/res/values/base-strings.xml index ae4485e0e..820b92125 100644 --- a/samples/browseable/NetworkConnect/res/values/base-strings.xml +++ b/samples/browseable/NetworkConnect/res/values/base-strings.xml @@ -22,7 +22,7 @@ This sample demonstrates how to connect to the network and fetch raw HTML using - HttpURLConnection. AsyncTask is used to perform the fetch on a background thread. + HttpsURLConnection. AsyncTask is used to perform the fetch on a background thread. ]]> diff --git a/samples/browseable/NetworkConnect/res/values/strings.xml b/samples/browseable/NetworkConnect/res/values/strings.xml index 1e7915afe..d2be145a8 100755 --- a/samples/browseable/NetworkConnect/res/values/strings.xml +++ b/samples/browseable/NetworkConnect/res/values/strings.xml @@ -18,8 +18,8 @@ <string name="welcome_message">Welcome to Network Connect! Click FETCH to fetch the first 500 characters of raw HTML from www.google.com. </string> - <string name="fetch_text">Fetch</string> <string name="clear_text">Clear</string> <string name="connection_error">Connection error.</string> + <string name="fetch_cancelled">Fetch cancelled.</string> </resources> diff --git a/samples/browseable/NetworkConnect/res/xml/network_security_config.xml b/samples/browseable/NetworkConnect/res/xml/network_security_config.xml new file mode 100644 index 000000000..4631b891f --- /dev/null +++ b/samples/browseable/NetworkConnect/res/xml/network_security_config.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<network-security-config> + <!--Set application-wide security config using base-config tag.--> + <base-config clearTextTrafficPermitted="false"/> + <!--Set domain-specific security config using domain-config tags. --> + <!--See https://developer.android.com/training/articles/security-config.html for more information.--> +</network-security-config>
\ No newline at end of file diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java deleted file mode 100644 index 17503c568..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/Log.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.example.android.common.logger; - -/** - * Helper class for a list (or tree) of LoggerNodes. - * - * <p>When this is set as the head of the list, - * an instance of it can function as a drop-in replacement for {@link android.util.Log}. - * Most of the methods in this class server only to map a method call in Log to its equivalent - * in LogNode.</p> - */ -public class Log { - // Grabbing the native values from Android's native logging facilities, - // to make for easy migration and interop. - public static final int NONE = -1; - public static final int VERBOSE = android.util.Log.VERBOSE; - public static final int DEBUG = android.util.Log.DEBUG; - public static final int INFO = android.util.Log.INFO; - public static final int WARN = android.util.Log.WARN; - public static final int ERROR = android.util.Log.ERROR; - public static final int ASSERT = android.util.Log.ASSERT; - - // Stores the beginning of the LogNode topology. - private static LogNode mLogNode; - - /** - * Returns the next LogNode in the linked list. - */ - public static LogNode getLogNode() { - return mLogNode; - } - - /** - * Sets the LogNode data will be sent to. - */ - public static void setLogNode(LogNode node) { - mLogNode = node; - } - - /** - * Instructs the LogNode to print the log data provided. Other LogNodes can - * be chained to the end of the LogNode as desired. - * - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void println(int priority, String tag, String msg, Throwable tr) { - if (mLogNode != null) { - mLogNode.println(priority, tag, msg, tr); - } - } - - /** - * Instructs the LogNode to print the log data provided. Other LogNodes can - * be chained to the end of the LogNode as desired. - * - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - */ - public static void println(int priority, String tag, String msg) { - println(priority, tag, msg, null); - } - - /** - * Prints a message at VERBOSE priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void v(String tag, String msg, Throwable tr) { - println(VERBOSE, tag, msg, tr); - } - - /** - * Prints a message at VERBOSE priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void v(String tag, String msg) { - v(tag, msg, null); - } - - - /** - * Prints a message at DEBUG priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void d(String tag, String msg, Throwable tr) { - println(DEBUG, tag, msg, tr); - } - - /** - * Prints a message at DEBUG priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void d(String tag, String msg) { - d(tag, msg, null); - } - - /** - * Prints a message at INFO priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void i(String tag, String msg, Throwable tr) { - println(INFO, tag, msg, tr); - } - - /** - * Prints a message at INFO priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void i(String tag, String msg) { - i(tag, msg, null); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void w(String tag, String msg, Throwable tr) { - println(WARN, tag, msg, tr); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void w(String tag, String msg) { - w(tag, msg, null); - } - - /** - * Prints a message at WARN priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void w(String tag, Throwable tr) { - w(tag, null, tr); - } - - /** - * Prints a message at ERROR priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void e(String tag, String msg, Throwable tr) { - println(ERROR, tag, msg, tr); - } - - /** - * Prints a message at ERROR priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void e(String tag, String msg) { - e(tag, msg, null); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void wtf(String tag, String msg, Throwable tr) { - println(ASSERT, tag, msg, tr); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. - */ - public static void wtf(String tag, String msg) { - wtf(tag, msg, null); - } - - /** - * Prints a message at ASSERT priority. - * - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public static void wtf(String tag, Throwable tr) { - wtf(tag, null, tr); - } -} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java deleted file mode 100644 index b302acd4b..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogFragment.java +++ /dev/null @@ -1,109 +0,0 @@ -/* -* Copyright 2013 The Android Open Source Project -* -* 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. -*/ -/* - * Copyright 2013 The Android Open Source Project - * - * 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.example.android.common.logger; - -import android.graphics.Typeface; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ScrollView; - -/** - * Simple fraggment which contains a LogView and uses is to output log data it receives - * through the LogNode interface. - */ -public class LogFragment extends Fragment { - - private LogView mLogView; - private ScrollView mScrollView; - - public LogFragment() {} - - public View inflateViews() { - mScrollView = new ScrollView(getActivity()); - ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - mScrollView.setLayoutParams(scrollParams); - - mLogView = new LogView(getActivity()); - ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); - logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; - mLogView.setLayoutParams(logParams); - mLogView.setClickable(true); - mLogView.setFocusable(true); - mLogView.setTypeface(Typeface.MONOSPACE); - - // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! - int paddingDips = 16; - double scale = getResources().getDisplayMetrics().density; - int paddingPixels = (int) ((paddingDips * (scale)) + .5); - mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); - mLogView.setCompoundDrawablePadding(paddingPixels); - - mLogView.setGravity(Gravity.BOTTOM); - mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); - - mScrollView.addView(mLogView); - return mScrollView; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View result = inflateViews(); - - mLogView.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - - @Override - public void afterTextChanged(Editable s) { - mScrollView.fullScroll(ScrollView.FOCUS_DOWN); - } - }); - return result; - } - - public LogView getLogView() { - return mLogView; - } -}
\ No newline at end of file diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java deleted file mode 100644 index bc37cabc0..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogNode.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.example.android.common.logger; - -/** - * Basic interface for a logging system that can output to one or more targets. - * Note that in addition to classes that will output these logs in some format, - * one can also implement this interface over a filter and insert that in the chain, - * such that no targets further down see certain data, or see manipulated forms of the data. - * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data - * it received to HTML and sent it along to the next node in the chain, without printing it - * anywhere. - */ -public interface LogNode { - - /** - * Instructs first LogNode in the list to print the log data provided. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - public void println(int priority, String tag, String msg, Throwable tr); - -} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java deleted file mode 100644 index c01542b91..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogView.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.example.android.common.logger; - -import android.app.Activity; -import android.content.Context; -import android.util.*; -import android.widget.TextView; - -/** Simple TextView which is used to output log data received through the LogNode interface. -*/ -public class LogView extends TextView implements LogNode { - - public LogView(Context context) { - super(context); - } - - public LogView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public LogView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - /** - * Formats the log data and prints it out to the LogView. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - - - String priorityStr = null; - - // For the purposes of this View, we want to print the priority as readable text. - switch(priority) { - case android.util.Log.VERBOSE: - priorityStr = "VERBOSE"; - break; - case android.util.Log.DEBUG: - priorityStr = "DEBUG"; - break; - case android.util.Log.INFO: - priorityStr = "INFO"; - break; - case android.util.Log.WARN: - priorityStr = "WARN"; - break; - case android.util.Log.ERROR: - priorityStr = "ERROR"; - break; - case android.util.Log.ASSERT: - priorityStr = "ASSERT"; - break; - default: - break; - } - - // Handily, the Log class has a facility for converting a stack trace into a usable string. - String exceptionStr = null; - if (tr != null) { - exceptionStr = android.util.Log.getStackTraceString(tr); - } - - // Take the priority, tag, message, and exception, and concatenate as necessary - // into one usable line of text. - final StringBuilder outputBuilder = new StringBuilder(); - - String delimiter = "\t"; - appendIfNotNull(outputBuilder, priorityStr, delimiter); - appendIfNotNull(outputBuilder, tag, delimiter); - appendIfNotNull(outputBuilder, msg, delimiter); - appendIfNotNull(outputBuilder, exceptionStr, delimiter); - - // In case this was originally called from an AsyncTask or some other off-UI thread, - // make sure the update occurs within the UI thread. - ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { - @Override - public void run() { - // Display the text we just generated within the LogView. - appendToLog(outputBuilder.toString()); - } - }))); - - if (mNext != null) { - mNext.println(priority, tag, msg, tr); - } - } - - public LogNode getNext() { - return mNext; - } - - public void setNext(LogNode node) { - mNext = node; - } - - /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since - * the logger takes so many arguments that might be null, this method helps cut out some of the - * agonizing tedium of writing the same 3 lines over and over. - * @param source StringBuilder containing the text to append to. - * @param addStr The String to append - * @param delimiter The String to separate the source and appended strings. A tab or comma, - * for instance. - * @return The fully concatenated String as a StringBuilder - */ - private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { - if (addStr != null) { - if (addStr.length() == 0) { - delimiter = ""; - } - - return source.append(addStr).append(delimiter); - } - return source; - } - - // The next LogNode in the chain. - LogNode mNext; - - /** Outputs the string as a new line of log data in the LogView. */ - public void appendToLog(String s) { - append("\n" + s); - } - - -} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java deleted file mode 100644 index 16a9e7ba2..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/LogWrapper.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.example.android.common.logger; - -import android.util.Log; - -/** - * Helper class which wraps Android's native Log utility in the Logger interface. This way - * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. - */ -public class LogWrapper implements LogNode { - - // For piping: The next node to receive Log data after this one has done its work. - private LogNode mNext; - - /** - * Returns the next LogNode in the linked list. - */ - public LogNode getNext() { - return mNext; - } - - /** - * Sets the LogNode data will be sent to.. - */ - public void setNext(LogNode node) { - mNext = node; - } - - /** - * Prints data out to the console using Android's native log mechanism. - * @param priority Log level of the data being logged. Verbose, Error, etc. - * @param tag Tag for for the log data. Can be used to organize log statements. - * @param msg The actual message to be logged. The actual message to be logged. - * @param tr If an exception was thrown, this can be sent along for the logging facilities - * to extract and print useful information. - */ - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - // There actually are log methods that don't take a msg parameter. For now, - // if that's the case, just convert null to the empty string and move on. - String useMsg = msg; - if (useMsg == null) { - useMsg = ""; - } - - // If an exeption was provided, convert that exception to a usable string and attach - // it to the end of the msg method. - if (tr != null) { - msg += "\n" + Log.getStackTraceString(tr); - } - - // This is functionally identical to Log.x(tag, useMsg); - // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) - Log.println(priority, tag, useMsg); - - // If this isn't the last node in the chain, move things along. - if (mNext != null) { - mNext.println(priority, tag, msg, tr); - } - } -} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java b/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java deleted file mode 100644 index 19967dcd4..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.common.logger/MessageOnlyLogFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.example.android.common.logger; - -/** - * Simple {@link LogNode} filter, removes everything except the message. - * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, - * just easy-to-read message updates as they're happening. - */ -public class MessageOnlyLogFilter implements LogNode { - - LogNode mNext; - - /** - * Takes the "next" LogNode as a parameter, to simplify chaining. - * - * @param next The next LogNode in the pipeline. - */ - public MessageOnlyLogFilter(LogNode next) { - mNext = next; - } - - public MessageOnlyLogFilter() { - } - - @Override - public void println(int priority, String tag, String msg, Throwable tr) { - if (mNext != null) { - getNext().println(Log.NONE, null, msg, null); - } - } - - /** - * Returns the next LogNode in the chain. - */ - public LogNode getNext() { - return mNext; - } - - /** - * Sets the LogNode data will be sent to.. - */ - public void setNext(LogNode node) { - mNext = node; - } - -} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/DownloadCallback.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/DownloadCallback.java new file mode 100644 index 000000000..b553e8500 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/DownloadCallback.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.networkconnect; + +import android.net.NetworkInfo; +import android.support.annotation.IntDef; + +/** + * Sample interface containing bare minimum methods needed for an asynchronous task + * to update the UI Context. + */ + +public interface DownloadCallback { + interface Progress { + int ERROR = -1; + int CONNECT_SUCCESS = 0; + int GET_INPUT_STREAM_SUCCESS = 1; + int PROCESS_INPUT_STREAM_IN_PROGRESS = 2; + int PROCESS_INPUT_STREAM_SUCCESS = 3; + } + + /** + * Indicates that the callback handler needs to update its appearance or information based on + * the result of the task. Expected to be called from the main thread. + */ + void updateFromDownload(String result); + + /** + * Get the device's active network status in the form of a NetworkInfo object. + */ + NetworkInfo getActiveNetworkInfo(); + + /** + * Indicate to callback handler any progress update. + * @param progressCode must be one of the constants defined in DownloadCallback.Progress. + * @param percentComplete must be 0-100. + */ + void onProgressUpdate(int progressCode, int percentComplete); + + /** + * Indicates that the download operation has finished. This method is called even if the + * download hasn't completed successfully. + */ + void finishDownloading(); + +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java index 3ad46463c..d490f5d10 100755 --- a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java +++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/MainActivity.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 The Android Open Source Project + * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,55 +16,42 @@ package com.example.android.networkconnect; -import android.os.AsyncTask; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Bundle; import android.support.v4.app.FragmentActivity; -import android.util.TypedValue; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; - -import com.example.android.common.logger.Log; -import com.example.android.common.logger.LogFragment; -import com.example.android.common.logger.LogWrapper; -import com.example.android.common.logger.MessageOnlyLogFilter; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; +import android.widget.TextView; /** - * Sample application demonstrating how to connect to the network and fetch raw - * HTML. It uses AsyncTask to do the fetch on a background thread. To establish - * the network connection, it uses HttpURLConnection. + * Sample Activity demonstrating how to connect to the network and fetch raw + * HTML. It uses a Fragment that encapsulates the network operations on an AsyncTask. * - * This sample uses the logging framework to display log output in the log - * fragment (LogFragment). + * This sample uses a TextView to display output. */ -public class MainActivity extends FragmentActivity { - - public static final String TAG = "Network Connect"; +public class MainActivity extends FragmentActivity implements DownloadCallback { - // Reference to the fragment showing events, so we can clear it with a button + // Reference to the TextView showing fetched data, so we can clear it with a button // as necessary. - private LogFragment mLogFragment; + private TextView mDataText; + + // Keep a reference to the NetworkFragment which owns the AsyncTask object + // that is used to execute network ops. + private NetworkFragment mNetworkFragment; + + // Boolean telling us whether a download is in progress, so we don't trigger overlapping + // downloads with consecutive button clicks. + private boolean mDownloading = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sample_main); - - // Initialize text fragment that displays intro text. - SimpleTextFragment introFragment = (SimpleTextFragment) - getSupportFragmentManager().findFragmentById(R.id.intro_fragment); - introFragment.setText(R.string.welcome_message); - introFragment.getTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16.0f); - - // Initialize the logging framework. - initializeLogging(); + mDataText = (TextView) findViewById(R.id.data_text); + mNetworkFragment = NetworkFragment.getInstance(getSupportFragmentManager(), "https://www.google.com"); } @Override @@ -79,111 +66,65 @@ public class MainActivity extends FragmentActivity { // When the user clicks FETCH, fetch the first 500 characters of // raw HTML from www.google.com. case R.id.fetch_action: - new DownloadTask().execute("http://www.google.com"); + startDownload(); return true; - // Clear the log view fragment. + // Clear the text and cancel download. case R.id.clear_action: - mLogFragment.getLogView().setText(""); - return true; + finishDownloading(); + mDataText.setText(""); + return true; } return false; } - /** - * Implementation of AsyncTask, to fetch the data in the background away from - * the UI thread. - */ - private class DownloadTask extends AsyncTask<String, Void, String> { - - @Override - protected String doInBackground(String... urls) { - try { - return loadFromNetwork(urls[0]); - } catch (IOException e) { - return getString(R.string.connection_error); - } - } - - /** - * Uses the logging framework to display the output of the fetch - * operation in the log fragment. - */ - @Override - protected void onPostExecute(String result) { - Log.i(TAG, result); + private void startDownload() { + if (!mDownloading && mNetworkFragment != null) { + // Execute the async download. + mNetworkFragment.startDownload(); + mDownloading = true; } } - /** Initiates the fetch operation. */ - private String loadFromNetwork(String urlString) throws IOException { - InputStream stream = null; - String str =""; - - try { - stream = downloadUrl(urlString); - str = readIt(stream, 500); - } finally { - if (stream != null) { - stream.close(); - } + @Override + public void updateFromDownload(String result) { + if (result != null) { + mDataText.setText(result); + } else { + mDataText.setText(getString(R.string.connection_error)); } - return str; } - /** - * Given a string representation of a URL, sets up a connection and gets - * an input stream. - * @param urlString A string representation of a URL. - * @return An InputStream retrieved from a successful HttpURLConnection. - * @throws java.io.IOException - */ - private InputStream downloadUrl(String urlString) throws IOException { - // BEGIN_INCLUDE(get_inputstream) - URL url = new URL(urlString); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setReadTimeout(10000 /* milliseconds */); - conn.setConnectTimeout(15000 /* milliseconds */); - conn.setRequestMethod("GET"); - conn.setDoInput(true); - // Start the query - conn.connect(); - InputStream stream = conn.getInputStream(); - return stream; - // END_INCLUDE(get_inputstream) + @Override + public NetworkInfo getActiveNetworkInfo() { + ConnectivityManager connectivityManager = + (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo; } - /** Reads an InputStream and converts it to a String. - * @param stream InputStream containing HTML from targeted site. - * @param len Length of string that this method returns. - * @return String concatenated according to len parameter. - * @throws java.io.IOException - * @throws java.io.UnsupportedEncodingException - */ - private String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException { - Reader reader = null; - reader = new InputStreamReader(stream, "UTF-8"); - char[] buffer = new char[len]; - reader.read(buffer); - return new String(buffer); + @Override + public void finishDownloading() { + mDownloading = false; + if (mNetworkFragment != null) { + mNetworkFragment.cancelDownload(); + } } - /** Create a chain of targets that will receive log data */ - public void initializeLogging() { - - // Using Log, front-end to the logging chain, emulates - // android.util.log method signatures. - - // Wraps Android's native log framework - LogWrapper logWrapper = new LogWrapper(); - Log.setLogNode(logWrapper); - - // A filter that strips out everything except the message text. - MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); - logWrapper.setNext(msgFilter); - - // On screen logging via a fragment with a TextView. - mLogFragment = - (LogFragment) getSupportFragmentManager().findFragmentById(R.id.log_fragment); - msgFilter.setNext(mLogFragment.getLogView()); + @Override + public void onProgressUpdate(int progressCode, int percentComplete) { + switch(progressCode) { + // You can add UI behavior for progress updates here. + case Progress.ERROR: + break; + case Progress.CONNECT_SUCCESS: + break; + case Progress.GET_INPUT_STREAM_SUCCESS: + break; + case Progress.PROCESS_INPUT_STREAM_IN_PROGRESS: + mDataText.setText("" + percentComplete + "%"); + break; + case Progress.PROCESS_INPUT_STREAM_SUCCESS: + break; + } } } diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java new file mode 100644 index 000000000..cc41075a1 --- /dev/null +++ b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/NetworkFragment.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.networkconnect; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Implementation of headless Fragment that runs an AsyncTask to fetch data from the network. + */ +public class NetworkFragment extends Fragment { + public static final String TAG = "NetworkFragment"; + + private static final String URL_KEY = "UrlKey"; + + private DownloadCallback mCallback; + private DownloadTask mDownloadTask; + private String mUrlString; + + /** + * Static initializer for NetworkFragment that sets the URL of the host it will be downloading + * from. + */ + public static NetworkFragment getInstance(FragmentManager fragmentManager, String url) { + // Recover NetworkFragment in case we are re-creating the Activity due to a config change. + // This is necessary because NetworkFragment might have a task that began running before + // the config change and has not finished yet. + // The NetworkFragment is recoverable via this method because it calls + // setRetainInstance(true) upon creation. + NetworkFragment networkFragment = (NetworkFragment) fragmentManager + .findFragmentByTag(NetworkFragment.TAG); + if (networkFragment == null) { + networkFragment = new NetworkFragment(); + Bundle args = new Bundle(); + args.putString(URL_KEY, url); + networkFragment.setArguments(args); + fragmentManager.beginTransaction().add(networkFragment, TAG).commit(); + } + return networkFragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Retain this Fragment across configuration changes in the host Activity. + setRetainInstance(true); + mUrlString = getArguments().getString(URL_KEY); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + // Host Activity will handle callbacks from task. + mCallback = (DownloadCallback)context; + } + + @Override + public void onDetach() { + super.onDetach(); + // Clear reference to host Activity. + mCallback = null; + } + + @Override + public void onDestroy() { + // Cancel task when Fragment is destroyed. + cancelDownload(); + super.onDestroy(); + } + + /** + * Start non-blocking execution of DownloadTask. + */ + public void startDownload() { + cancelDownload(); + mDownloadTask = new DownloadTask(); + mDownloadTask.execute(mUrlString); + } + + /** + * Cancel (and interrupt if necessary) any ongoing DownloadTask execution. + */ + public void cancelDownload() { + if (mDownloadTask != null) { + mDownloadTask.cancel(true); + mDownloadTask = null; + } + } + + /** + * Implementation of AsyncTask that runs a network operation on a background thread. + */ + private class DownloadTask extends AsyncTask<String, Integer, DownloadTask.Result> { + + /** + * Wrapper class that serves as a union of a result value and an exception. When the + * download task has completed, either the result value or exception can be a non-null + * value. This allows you to pass exceptions to the UI thread that were thrown during + * doInBackground(). + */ + class Result { + public String mResultValue; + public Exception mException; + public Result(String resultValue) { + mResultValue = resultValue; + } + public Result(Exception exception) { + mException = exception; + } + } + + /** + * Cancel background network operation if we do not have network connectivity. + */ + @Override + protected void onPreExecute() { + if (mCallback != null) { + NetworkInfo networkInfo = mCallback.getActiveNetworkInfo(); + if (networkInfo == null || !networkInfo.isConnected() || + (networkInfo.getType() != ConnectivityManager.TYPE_WIFI + && networkInfo.getType() != ConnectivityManager.TYPE_MOBILE)) { + // If no connectivity, cancel task and update Callback with null data. + mCallback.updateFromDownload(null); + cancel(true); + } + } + } + + /** + * Defines work to perform on the background thread. + */ + @Override + protected Result doInBackground(String... urls) { + Result result = null; + if (!isCancelled() && urls != null && urls.length > 0) { + String urlString = urls[0]; + try { + URL url = new URL(urlString); + String resultString = downloadUrl(url); + if (resultString != null) { + result = new Result(resultString); + } else { + throw new IOException("No response received."); + } + } catch(Exception e) { + result = new Result(e); + } + } + return result; + } + + /** + * Send DownloadCallback a progress update. + */ + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + if (values.length >= 2) { + mCallback.onProgressUpdate(values[0], values[1]); + } + } + + /** + * Updates the DownloadCallback with the result. + */ + @Override + protected void onPostExecute(Result result) { + if (result != null && mCallback != null) { + if (result.mException != null) { + mCallback.updateFromDownload(result.mException.getMessage()); + } else if (result.mResultValue != null) { + mCallback.updateFromDownload(result.mResultValue); + } + mCallback.finishDownloading(); + } + } + + /** + * Override to add special behavior for cancelled AsyncTask. + */ + @Override + protected void onCancelled(Result result) { + } + + /** + * Given a URL, sets up a connection and gets the HTTP response body from the server. + * If the network request is successful, it returns the response body in String form. Otherwise, + * it will throw an IOException. + */ + private String downloadUrl(URL url) throws IOException { + InputStream stream = null; + HttpsURLConnection connection = null; + String result = null; + try { + connection = (HttpsURLConnection) url.openConnection(); + // Timeout for reading InputStream arbitrarily set to 3000ms. + connection.setReadTimeout(3000); + // Timeout for connection.connect() arbitrarily set to 3000ms. + connection.setConnectTimeout(3000); + // For this use case, set HTTP method to GET. + connection.setRequestMethod("GET"); + // Already true by default but setting just in case; needs to be true since this request + // is carrying an input (response) body. + connection.setDoInput(true); + // Open communications link (network traffic occurs here). + connection.connect(); + publishProgress(DownloadCallback.Progress.CONNECT_SUCCESS); + int responseCode = connection.getResponseCode(); + if (responseCode != HttpsURLConnection.HTTP_OK) { + throw new IOException("HTTP error code: " + responseCode); + } + // Retrieve the response body as an InputStream. + stream = connection.getInputStream(); + publishProgress(DownloadCallback.Progress.GET_INPUT_STREAM_SUCCESS, 0); + if (stream != null) { + // Converts Stream to String with max length of 500. + result = readStream(stream, 500); + publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_SUCCESS, 0); + } + } finally { + // Close Stream and disconnect HTTPS connection. + if (stream != null) { + stream.close(); + } + if (connection != null) { + connection.disconnect(); + } + } + return result; + } + + /** + * Converts the contents of an InputStream to a String. + */ + private String readStream(InputStream stream, int maxLength) throws IOException { + String result = null; + // Read InputStream using the UTF-8 charset. + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + // Create temporary buffer to hold Stream data with specified max length. + char[] buffer = new char[maxLength]; + // Populate temporary buffer with Stream data. + int numChars = 0; + int readSize = 0; + while (numChars < maxLength && readSize != -1) { + numChars += readSize; + int pct = (100 * numChars) / maxLength; + publishProgress(DownloadCallback.Progress.PROCESS_INPUT_STREAM_IN_PROGRESS, pct); + readSize = reader.read(buffer, numChars, buffer.length - numChars); + } + if (numChars != -1) { + // The stream was not empty. + // Create String that is actual length of response body if actual length was less than + // max length. + numChars = Math.min(numChars, maxLength); + result = new String(buffer, 0, numChars); + } + return result; + } + } +} diff --git a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java b/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java deleted file mode 100644 index 320293760..000000000 --- a/samples/browseable/NetworkConnect/src/com.example.android.networkconnect/SimpleTextFragment.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2013 The Android Open Source Project - * - * 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.example.android.networkconnect; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -/** - * Simple fragment containing only a TextView. Used by TextPagerAdapter to create - * tutorial-style pages for apps. - */ -public class SimpleTextFragment extends Fragment { - - // Contains the text that will be displayed by this Fragment - String mText; - - // Contains a resource ID for the text that will be displayed by this fragment. - int mTextId = -1; - - // Keys which will be used to store/retrieve text passed in via setArguments. - public static final String TEXT_KEY = "text"; - public static final String TEXT_ID_KEY = "text_id"; - - // For situations where the app wants to modify text at Runtime, exposing the TextView. - private TextView mTextView; - - public SimpleTextFragment() { - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Before initializing the textView, check if any arguments were provided via setArguments. - processArguments(); - - // Create a new TextView and set its text to whatever was provided. - mTextView = new TextView(getActivity()); - mTextView.setGravity(Gravity.CENTER); - - if (mText != null) { - mTextView.setText(mText); - Log.i("SimpleTextFragment", mText); - } - return mTextView; - } - - public TextView getTextView() { - return mTextView; - } - - /** - * Changes the text for this TextView, according to the resource ID provided. - * @param stringId A resource ID representing the text content for this Fragment's TextView. - */ - public void setText(int stringId) { - getTextView().setText(getActivity().getString(stringId)); - } - - /** - * Processes the arguments passed into this Fragment via setArguments method. - * Currently the method only looks for text or a textID, nothing else. - */ - public void processArguments() { - // For most objects we'd handle the multiple possibilities for initialization variables - // as multiple constructors. For Fragments, however, it's customary to use - // setArguments / getArguments. - if (getArguments() != null) { - Bundle args = getArguments(); - if (args.containsKey(TEXT_KEY)) { - mText = args.getString(TEXT_KEY); - Log.d("Constructor", "Added Text."); - } else if (args.containsKey(TEXT_ID_KEY)) { - mTextId = args.getInt(TEXT_ID_KEY); - mText = getString(mTextId); - } - } - } -}
\ No newline at end of file diff --git a/samples/browseable/Notifications/Application/AndroidManifest.xml b/samples/browseable/Notifications/Application/AndroidManifest.xml index 6a17ad8c3..4b0a9b3a9 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" > <uses-sdk android:minSdkVersion="18" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <uses-permission android:name="android.permission.VIBRATE" /> diff --git a/samples/browseable/Notifications/Application/res/values/base-strings.xml b/samples/browseable/Notifications/Application/res/values/base-strings.xml index 9ab282f7e..c5ef0694c 100644 --- a/samples/browseable/Notifications/Application/res/values/base-strings.xml +++ b/samples/browseable/Notifications/Application/res/values/base-strings.xml @@ -16,7 +16,7 @@ --> <resources> - <string name="app_name">Wearable Notifications</string> + <string name="app_name">Notifications</string> <string name="intro_message"> <![CDATA[ diff --git a/samples/browseable/Notifications/Wearable/AndroidManifest.xml b/samples/browseable/Notifications/Wearable/AndroidManifest.xml index a446fd9bb..0bce3ea94 100644 --- a/samples/browseable/Notifications/Wearable/AndroidManifest.xml +++ b/samples/browseable/Notifications/Wearable/AndroidManifest.xml @@ -17,8 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.support.wearable.notifications" > - <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="22" /> + <uses-sdk android:minSdkVersion="22" + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -28,6 +28,10 @@ android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" > + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + <activity android:name=".MainActivity" android:label="@string/app_name" > diff --git a/samples/browseable/Notifications/_index.jd b/samples/browseable/Notifications/_index.jd index e2bfa36b3..50d2020fb 100644 --- a/samples/browseable/Notifications/_index.jd +++ b/samples/browseable/Notifications/_index.jd @@ -1,5 +1,5 @@ -page.tags="Wearable Notifications" +page.tags="Notifications" sample.group=Wearable @jd:body diff --git a/samples/browseable/PdfRendererBasic/src/com.example.android.pdfrendererbasic/PdfRendererBasicFragment.java b/samples/browseable/PdfRendererBasic/src/com.example.android.pdfrendererbasic/PdfRendererBasicFragment.java index e413c6599..20b0a7f45 100644 --- a/samples/browseable/PdfRendererBasic/src/com.example.android.pdfrendererbasic/PdfRendererBasicFragment.java +++ b/samples/browseable/PdfRendererBasic/src/com.example.android.pdfrendererbasic/PdfRendererBasicFragment.java @@ -30,7 +30,10 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; /** * This fragment has a big {@ImageView} that shows PDF pages, and 2 {@link android.widget.Button}s to move between @@ -44,6 +47,11 @@ public class PdfRendererBasicFragment extends Fragment implements View.OnClickLi private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index"; /** + * The filename of the PDF. + */ + private static final String FILENAME = "sample.pdf"; + + /** * File descriptor of the PDF. */ private ParcelFileDescriptor mFileDescriptor; @@ -136,7 +144,21 @@ public class PdfRendererBasicFragment extends Fragment implements View.OnClickLi */ private void openRenderer(Context context) throws IOException { // In this sample, we read a PDF from the assets directory. - mFileDescriptor = context.getAssets().openFd("sample.pdf").getParcelFileDescriptor(); + File file = new File(context.getCacheDir(), FILENAME); + if (!file.exists()) { + // Since PdfRenderer cannot handle the compressed asset file directly, we copy it into + // the cache directory. + InputStream asset = context.getAssets().open(FILENAME); + FileOutputStream output = new FileOutputStream(file); + final byte[] buffer = new byte[1024]; + int size; + while ((size = asset.read(buffer)) != -1) { + output.write(buffer, 0, size); + } + asset.close(); + output.close(); + } + mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); // This is the PdfRenderer we use to render the PDF. mPdfRenderer = new PdfRenderer(mFileDescriptor); } diff --git a/samples/browseable/PermissionRequest/res/values/strings.xml b/samples/browseable/PermissionRequest/res/values/strings.xml index c3e5574dc..248876601 100644 --- a/samples/browseable/PermissionRequest/res/values/strings.xml +++ b/samples/browseable/PermissionRequest/res/values/strings.xml @@ -15,6 +15,7 @@ limitations under the License. --> <resources> + <string name="permission_message">This sample app uses camera.</string> <string name="confirmation">This web page wants to use following resources:\n\n%s</string> <string name="allow">Allow</string> <string name="deny">Deny</string> diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java index 7dae56efd..ca173b424 100644 --- a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java +++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/ConfirmationDialogFragment.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.example.android.permissionrequest; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.text.TextUtils; @@ -32,7 +34,7 @@ public class ConfirmationDialogFragment extends DialogFragment { /** * Creates a new instance of ConfirmationDialogFragment. * - * @param resources The list of resources requested by PermissionRequeste. + * @param resources The list of resources requested by PermissionRequest. * @return A new instance. */ public static ConfirmationDialogFragment newInstance(String[] resources) { @@ -43,21 +45,22 @@ public class ConfirmationDialogFragment extends DialogFragment { return fragment; } + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - String[] resources = getArguments().getStringArray(ARG_RESOURCES); + final String[] resources = getArguments().getStringArray(ARG_RESOURCES); return new AlertDialog.Builder(getActivity()) .setMessage(getString(R.string.confirmation, TextUtils.join("\n", resources))) .setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - ((Listener) getParentFragment()).onConfirmation(false); + ((Listener) getParentFragment()).onConfirmation(false, resources); } }) .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - ((Listener) getParentFragment()).onConfirmation(true); + ((Listener) getParentFragment()).onConfirmation(true, resources); } }) .create(); @@ -66,14 +69,15 @@ public class ConfirmationDialogFragment extends DialogFragment { /** * Callback for the user's response. */ - public interface Listener { + interface Listener { /** - * Called when the PermissoinRequest is allowed or denied by the user. + * Called when the PermissionRequest is allowed or denied by the user. * - * @param allowed True if the user allowed the request. + * @param allowed True if the user allowed the request. + * @param resources The resources to be granted. */ - public void onConfirmation(boolean allowed); + void onConfirmation(boolean allowed, String[] resources); } } diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MessageDialogFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MessageDialogFragment.java new file mode 100644 index 000000000..31d0bcbf7 --- /dev/null +++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/MessageDialogFragment.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.permissionrequest; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.v4.app.DialogFragment; + +/** + * Shows a dialog with a brief message. + */ +public class MessageDialogFragment extends DialogFragment { + + private static final String ARG_MESSAGE_RES_ID = "message_res_id"; + + public static MessageDialogFragment newInstance(@StringRes int message) { + MessageDialogFragment fragment = new MessageDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_MESSAGE_RES_ID, message); + fragment.setArguments(args); + return fragment; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getContext()) + .setMessage(getArguments().getInt(ARG_MESSAGE_RES_ID)) + .setCancelable(false) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ((Listener) getParentFragment()).onOkClicked(); + } + }) + .create(); + } + + interface Listener { + void onOkClicked(); + } + +} diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java index 44f1d6ec5..418c90d34 100644 --- a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java +++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/PermissionRequestFragment.java @@ -16,12 +16,15 @@ package com.example.android.permissionrequest; +import android.Manifest; import android.annotation.SuppressLint; +import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -37,12 +40,14 @@ import com.example.android.common.logger.Log; * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}. */ public class PermissionRequestFragment extends Fragment - implements ConfirmationDialogFragment.Listener { + implements ConfirmationDialogFragment.Listener, MessageDialogFragment.Listener { private static final String TAG = PermissionRequestFragment.class.getSimpleName(); private static final String FRAGMENT_DIALOG = "dialog"; + private static final int REQUEST_CAMERA_PERMISSION = 1; + /** * We use this web server to serve HTML files in the assets folder. This is because we cannot * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs. @@ -67,7 +72,7 @@ public class PermissionRequestFragment extends Fragment @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_permission_request, container, false); } @@ -85,7 +90,14 @@ public class PermissionRequestFragment extends Fragment final int port = 8080; mWebServer = new SimpleWebServer(port, getResources().getAssets()); mWebServer.start(); - mWebView.loadUrl("http://localhost:" + port + "/sample.html"); + // This is for runtime permission on Marshmallow and above; It is not directly related to + // PermissionRequest API. + if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + requestCameraPermission(); + } else { + mWebView.loadUrl("http://localhost:" + port + "/sample.html"); + } } @Override @@ -94,6 +106,32 @@ public class PermissionRequestFragment extends Fragment super.onPause(); } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + // This is for runtime permission on Marshmallow and above; It is not directly related to + // PermissionRequest API. + if (requestCode == REQUEST_CAMERA_PERMISSION) { + if (permissions.length != 1 || grantResults.length != 1 || + grantResults[0] != PackageManager.PERMISSION_GRANTED) { + Log.e(TAG, "Camera permission not granted."); + } else if (mWebView != null && mWebServer != null) { + mWebView.loadUrl("http://localhost:" + mWebServer.getPort() + "/sample.html"); + } + } else { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + + private void requestCameraPermission() { + if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { + MessageDialogFragment.newInstance(R.string.permission_message) + .show(getChildFragmentManager(), FRAGMENT_DIALOG); + } else { + requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); + } + } + @SuppressLint("SetJavaScriptEnabled") private static void configureWebSettings(WebSettings settings) { settings.setJavaScriptEnabled(true); @@ -110,8 +148,16 @@ public class PermissionRequestFragment extends Fragment public void onPermissionRequest(PermissionRequest request) { Log.i(TAG, "onPermissionRequest"); mPermissionRequest = request; - ConfirmationDialogFragment.newInstance(request.getResources()) - .show(getChildFragmentManager(), FRAGMENT_DIALOG); + final String[] requestedResources = request.getResources(); + for (String r : requestedResources) { + if (r.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) { + // In this sample, we only accept video capture request. + ConfirmationDialogFragment + .newInstance(new String[]{PermissionRequest.RESOURCE_VIDEO_CAPTURE}) + .show(getChildFragmentManager(), FRAGMENT_DIALOG); + break; + } + } } // This method is called when the permission request is canceled by the web content. @@ -155,9 +201,14 @@ public class PermissionRequestFragment extends Fragment }; @Override - public void onConfirmation(boolean allowed) { + public void onOkClicked() { + requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); + } + + @Override + public void onConfirmation(boolean allowed, String[] resources) { if (allowed) { - mPermissionRequest.grant(mPermissionRequest.getResources()); + mPermissionRequest.grant(resources); Log.d(TAG, "Permission granted."); } else { mPermissionRequest.deny(); @@ -174,7 +225,7 @@ public class PermissionRequestFragment extends Fragment * For testing. */ public interface ConsoleMonitor { - public void onConsoleMessage(ConsoleMessage message); + void onConsoleMessage(ConsoleMessage message); } } diff --git a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java index 36b7c4693..b02275af5 100644 --- a/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java +++ b/samples/browseable/PermissionRequest/src/com.example.android.permissionrequest/SimpleWebServer.java @@ -90,6 +90,10 @@ public class SimpleWebServer implements Runnable { } } + public int getPort() { + return mPort; + } + @Override public void run() { try { diff --git a/samples/browseable/RepeatingAlarm/res/menu/main.xml b/samples/browseable/RepeatingAlarm/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/RepeatingAlarm/res/menu/main.xml +++ b/samples/browseable/RepeatingAlarm/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/RuntimePermissionsWear/Wearable/AndroidManifest.xml b/samples/browseable/RuntimePermissionsWear/Wearable/AndroidManifest.xml index e654253fc..385d3ce19 100644 --- a/samples/browseable/RuntimePermissionsWear/Wearable/AndroidManifest.xml +++ b/samples/browseable/RuntimePermissionsWear/Wearable/AndroidManifest.xml @@ -31,6 +31,10 @@ android:theme="@android:style/Theme.DeviceDefault" > <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + + <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/AndroidManifest.xml b/samples/browseable/ShareActionProvider/AndroidManifest.xml index 18dfc27fd..95283386f 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/AndroidManifest.xml +++ b/samples/browseable/ShareActionProvider/AndroidManifest.xml @@ -16,14 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.android.actionbarcompat.shareactionprovider" + package="com.example.android.shareactionprovider" android:versionCode="1" android:versionName="1.0"> - <!-- - ActionBarCompat provides an Action Bar from API v7 onwards - --> - <!-- Min/target SDK versions (<uses-sdk>) managed by build.gradle --> + <permission android:name="com.example.android.shareactionprovider.ASSET_ACCESS" + android:protectionLevel="signature" /> <application android:icon="@drawable/ic_launcher" @@ -41,12 +39,14 @@ </activity> <!-- ContentProvider which serves files from this application's asset folder --> + <!-- Allow the access to only the apps having the same signature for security --> <provider android:name=".content.AssetProvider" - android:authorities="com.example.android.actionbarcompat.shareactionprovider" + android:authorities="com.example.android.shareactionprovider" android:grantUriPermissions="true" - android:exported="true" /> + android:exported="true" + android:permission="com.example.android.shareactionprovider.ASSET_ACCESS" /> </application> -</manifest>
\ No newline at end of file +</manifest> diff --git a/samples/browseable/ShareActionProvider/_index.jd b/samples/browseable/ShareActionProvider/_index.jd new file mode 100644 index 000000000..ae52e4ec8 --- /dev/null +++ b/samples/browseable/ShareActionProvider/_index.jd @@ -0,0 +1,10 @@ + +page.tags="ShareActionProvider" +sample.group=UI +@jd:body + +<p> + + This sample shows you how a provide a context-sensitive ShareActionProvider. + + </p> diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png Binary files differindex 48db73f18..48db73f18 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/ic_launcher.png +++ b/samples/browseable/ShareActionProvider/res/drawable-hdpi/ic_launcher.png diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/tile.9.png b/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png Binary files differindex 135862883..135862883 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-hdpi/tile.9.png +++ b/samples/browseable/ShareActionProvider/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-mdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png Binary files differindex 674b1eee3..674b1eee3 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-mdpi/ic_launcher.png +++ b/samples/browseable/ShareActionProvider/res/drawable-mdpi/ic_launcher.png diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xhdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png Binary files differindex e76105d9f..e76105d9f 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xhdpi/ic_launcher.png +++ b/samples/browseable/ShareActionProvider/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png b/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png Binary files differindex 67605d802..67605d802 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png +++ b/samples/browseable/ShareActionProvider/res/drawable-xxhdpi/ic_launcher.png diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_image.xml b/samples/browseable/ShareActionProvider/res/layout/item_image.xml index f7940e75e..f7940e75e 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_image.xml +++ b/samples/browseable/ShareActionProvider/res/layout/item_image.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_text.xml b/samples/browseable/ShareActionProvider/res/layout/item_text.xml index 00c6a387d..00c6a387d 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/item_text.xml +++ b/samples/browseable/ShareActionProvider/res/layout/item_text.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/sample_main.xml b/samples/browseable/ShareActionProvider/res/layout/sample_main.xml index 902e8ab27..902e8ab27 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/layout/sample_main.xml +++ b/samples/browseable/ShareActionProvider/res/layout/sample_main.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/menu/main_menu.xml b/samples/browseable/ShareActionProvider/res/menu/main_menu.xml index acd213417..4b8526736 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/menu/main_menu.xml +++ b/samples/browseable/ShareActionProvider/res/menu/main_menu.xml @@ -16,8 +16,7 @@ --> <!-- - As we're using ActionBarCompat, any action item attributes come from ActionBarCompat's XML - namespace instead of the android namespace. Here we've added a new support namespace added to + Here we've added a new support namespace added to the menu element allowing us to use the 'showAsAction' attribute in a backwards compatible way. Any other action item attributes used should be referenced from this namespace too (actionProviderClass, actionViewClass, actionLayout). @@ -26,7 +25,7 @@ xmlns:support="http://schemas.android.com/apk/res-auto"> <!-- - To use ShareActionProvider provided by ActionBarCompat, we reference the class by set the + To use ShareActionProvider, we reference the class by set the support:actionProviderClass attribute with the full class name of ShareActionProvider. --> <item @@ -35,4 +34,4 @@ support:actionProviderClass="android.support.v7.widget.ShareActionProvider" support:showAsAction="always" /> -</menu>
\ No newline at end of file +</menu> diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-dimens.xml b/samples/browseable/ShareActionProvider/res/values-sw600dp/template-dimens.xml index 22074a2bd..22074a2bd 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-dimens.xml +++ b/samples/browseable/ShareActionProvider/res/values-sw600dp/template-dimens.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-styles.xml b/samples/browseable/ShareActionProvider/res/values-sw600dp/template-styles.xml index 03d197418..03d197418 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-sw600dp/template-styles.xml +++ b/samples/browseable/ShareActionProvider/res/values-sw600dp/template-styles.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml b/samples/browseable/ShareActionProvider/res/values-v11/template-styles.xml index 8c1ea66f2..8c1ea66f2 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v11/template-styles.xml +++ b/samples/browseable/ShareActionProvider/res/values-v11/template-styles.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v21/base-colors.xml b/samples/browseable/ShareActionProvider/res/values-v21/base-colors.xml index 8b6ec3f85..8b6ec3f85 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v21/base-colors.xml +++ b/samples/browseable/ShareActionProvider/res/values-v21/base-colors.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v21/base-template-styles.xml b/samples/browseable/ShareActionProvider/res/values-v21/base-template-styles.xml index c778e4f98..c778e4f98 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values-v21/base-template-styles.xml +++ b/samples/browseable/ShareActionProvider/res/values-v21/base-template-styles.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/base-strings.xml b/samples/browseable/ShareActionProvider/res/values/base-strings.xml index 21d17e4f5..ab5e2fbdc 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/base-strings.xml +++ b/samples/browseable/ShareActionProvider/res/values/base-strings.xml @@ -16,13 +16,12 @@ --> <resources> - <string name="app_name">ActionBarCompat-ShareActionProvider</string> + <string name="app_name">ShareActionProvider</string> <string name="intro_message"> <![CDATA[ - This sample shows you how a provide a context-sensitive ShareActionProvider with - ActionBarCompat, backwards compatible to API v7. + This sample shows you how a provide a context-sensitive ShareActionProvider. ]]> diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/strings.xml b/samples/browseable/ShareActionProvider/res/values/strings.xml index 298596f04..298596f04 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/strings.xml +++ b/samples/browseable/ShareActionProvider/res/values/strings.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-dimens.xml b/samples/browseable/ShareActionProvider/res/values/template-dimens.xml index 39e710b5c..39e710b5c 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-dimens.xml +++ b/samples/browseable/ShareActionProvider/res/values/template-dimens.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-styles.xml b/samples/browseable/ShareActionProvider/res/values/template-styles.xml index 6e7d593dd..6e7d593dd 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/res/values/template-styles.xml +++ b/samples/browseable/ShareActionProvider/res/values/template-styles.xml diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/MainActivity.java index 545764cad..3ac28ea62 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/MainActivity.java +++ b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/MainActivity.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.example.android.actionbarcompat.shareactionprovider; +package com.example.android.shareactionprovider; + +import com.example.android.shareactionprovider.content.ContentItem; import android.content.Intent; import android.os.Bundle; import android.support.v4.view.MenuItemCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.ShareActionProvider; import android.view.LayoutInflater; import android.view.Menu; @@ -31,8 +33,6 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.example.android.actionbarcompat.shareactionprovider.content.ContentItem; - import java.util.ArrayList; /** @@ -43,10 +43,8 @@ import java.util.ArrayList; * text. When a new item is selected in the ViewPager, the ShareActionProvider is updated with * a share intent specific to that content. * <p> - * This Activity extends from {@link ActionBarActivity}, which provides all of the function - * necessary to display a compatible Action Bar on devices running Android v2.1+. */ -public class MainActivity extends ActionBarActivity { +public class MainActivity extends AppCompatActivity { // The items to be displayed in the ViewPager private final ArrayList<ContentItem> mItems = getSampleContent(); @@ -65,7 +63,7 @@ public class MainActivity extends ActionBarActivity { ViewPager vp = (ViewPager) findViewById(R.id.viewpager); // Set an OnPageChangeListener so we are notified when a new item is selected - vp.setOnPageChangeListener(mOnPageChangeListener); + vp.addOnPageChangeListener(mOnPageChangeListener); // Finally set the adapter so the ViewPager can display items vp.setAdapter(mPagerAdapter); diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/content/AssetProvider.java index b60f7d78b..21e3ab2a8 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/AssetProvider.java +++ b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/content/AssetProvider.java @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.example.android.actionbarcompat.shareactionprovider.content; +package com.example.android.shareactionprovider.content; import android.content.ContentProvider; import android.content.ContentValues; +import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.database.Cursor; import android.net.Uri; +import android.support.annotation.NonNull; import android.text.TextUtils; import java.io.FileNotFoundException; @@ -41,38 +43,41 @@ public class AssetProvider extends ContentProvider { } @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { + public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { // Do not support delete requests. return 0; } @Override - public String getType(Uri uri) { + public String getType(@NonNull Uri uri) { // Do not support returning the data type return null; } @Override - public Uri insert(Uri uri, ContentValues values) { + public Uri insert(@NonNull Uri uri, ContentValues values) { // Do not support insert requests. return null; } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + public Cursor query(@NonNull Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { // Do not support query requests. return null; } @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + public int update(@NonNull Uri uri, ContentValues values, String selection, + String[] selectionArgs) { // Do not support update requests. return 0; } @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException { + public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) + throws FileNotFoundException { // The asset file name should be the last path segment final String assetName = uri.getLastPathSegment(); @@ -83,7 +88,11 @@ public class AssetProvider extends ContentProvider { try { // Try and return a file descriptor for the given asset name - AssetManager am = getContext().getAssets(); + Context context = getContext(); + if (context == null) { + return super.openAssetFile(uri, mode); + } + AssetManager am = context.getAssets(); return am.openFd(assetName); } catch (IOException e) { e.printStackTrace(); diff --git a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/content/ContentItem.java index 756a9e66d..5806088b7 100644 --- a/samples/browseable/ActionBarCompat-ShareActionProvider/src/com.example.android.actionbarcompat.shareactionprovider/content/ContentItem.java +++ b/samples/browseable/ShareActionProvider/src/com.example.android.shareactionprovider/content/ContentItem.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.example.android.actionbarcompat.shareactionprovider.content; +package com.example.android.shareactionprovider.content; import android.content.Context; import android.content.Intent; @@ -33,7 +33,7 @@ public class ContentItem { public final int contentType; public final int contentResourceId; - public final String contentAssetFilePath; + private final String mContentAssetFilePath; /** * Creates a ContentItem with the specified type, referencing a resource id. @@ -44,7 +44,7 @@ public class ContentItem { public ContentItem(int type, int resourceId) { contentType = type; contentResourceId = resourceId; - contentAssetFilePath = null; + mContentAssetFilePath = null; } /** @@ -55,7 +55,7 @@ public class ContentItem { */ public ContentItem(int type, String assetFilePath) { contentType = type; - contentAssetFilePath = assetFilePath; + mContentAssetFilePath = assetFilePath; contentResourceId = 0; } @@ -63,9 +63,9 @@ public class ContentItem { * @return Uri to the content */ public Uri getContentUri() { - if (!TextUtils.isEmpty(contentAssetFilePath)) { + if (!TextUtils.isEmpty(mContentAssetFilePath)) { // If this content has an asset, then return a AssetProvider Uri - return Uri.parse("content://" + AssetProvider.CONTENT_URI + "/" + contentAssetFilePath); + return Uri.parse("content://" + AssetProvider.CONTENT_URI + "/" + mContentAssetFilePath); } else { return null; } diff --git a/samples/browseable/SpeedTracker/Application/AndroidManifest.xml b/samples/browseable/SpeedTracker/Application/AndroidManifest.xml index debd11d6f..bf86a10cc 100644 --- a/samples/browseable/SpeedTracker/Application/AndroidManifest.xml +++ b/samples/browseable/SpeedTracker/Application/AndroidManifest.xml @@ -5,7 +5,7 @@ <uses-sdk android:minSdkVersion="18" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <!-- BEGIN_INCLUDE(manifest) --> diff --git a/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml b/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml index 17a8f6a9d..ef2480a61 100644 --- a/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml +++ b/samples/browseable/SpeedTracker/Application/res/layout/main_activity.xml @@ -43,10 +43,10 @@ </RelativeLayout> <fragment + class="com.google.android.gms.maps.SupportMapFragment" android:id="@+id/map" android:layout_below="@+id/top_container" - android:layout_width="fill_parent" - android:layout_height="match_parent" - android:name="com.google.android.gms.maps.MapFragment"/> + android:layout_width="match_parent" + android:layout_height="match_parent"/> -</RelativeLayout>
\ No newline at end of file +</RelativeLayout> diff --git a/samples/browseable/SpeedTracker/Application/res/values/base-strings.xml b/samples/browseable/SpeedTracker/Application/res/values/base-strings.xml index 61f103ceb..b5e0a236b 100644 --- a/samples/browseable/SpeedTracker/Application/res/values/base-strings.xml +++ b/samples/browseable/SpeedTracker/Application/res/values/base-strings.xml @@ -16,14 +16,13 @@ --> <resources> - <string name="app_name">Speed Tracker</string> + <string name="app_name">SpeedTracker</string> <string name="intro_message"> <![CDATA[ This sample uses the FusedLocation APIs of Google Play Services -on those devices that have a hardware GPS built in. In those -cases, this sample provides a simple screen that shows the +to gather location and speed. The sample provides a simple screen that shows the current speed of the device on the watch. User can set a speed limit and if the speed approaches that limit, it changes the color to yellow and if it exceeds the limit, it turns red. User diff --git a/samples/browseable/SpeedTracker/Application/src/com.example.android.wearable.speedtracker/PhoneMainActivity.java b/samples/browseable/SpeedTracker/Application/src/com.example.android.wearable.speedtracker/PhoneMainActivity.java index c645bdd62..b915d26d0 100644 --- a/samples/browseable/SpeedTracker/Application/src/com.example.android.wearable.speedtracker/PhoneMainActivity.java +++ b/samples/browseable/SpeedTracker/Application/src/com.example.android.wearable.speedtracker/PhoneMainActivity.java @@ -18,7 +18,8 @@ package com.example.android.wearable.speedtracker; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.MapFragment; +import com.google.android.gms.maps.SupportMapFragment; +import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.PolylineOptions; @@ -46,19 +47,21 @@ import java.util.List; * deleted. */ public class PhoneMainActivity extends AppCompatActivity implements - DatePickerDialog.OnDateSetListener { + DatePickerDialog.OnDateSetListener, OnMapReadyCallback { private static final String TAG = "PhoneMainActivity"; private static final int BOUNDING_BOX_PADDING_PX = 50; private TextView mSelectedDateText; private GoogleMap mMap; + private SupportMapFragment mMapFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); mSelectedDateText = (TextView) findViewById(R.id.selected_date); - mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap(); + mMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); + mMapFragment.getMapAsync(this); } public void onClick(View view) { @@ -132,4 +135,9 @@ public class PhoneMainActivity extends AppCompatActivity implements }.execute(calendar); } + + @Override + public void onMapReady(GoogleMap googleMap) { + mMap = googleMap; + } } diff --git a/samples/browseable/SpeedTracker/Wearable/AndroidManifest.xml b/samples/browseable/SpeedTracker/Wearable/AndroidManifest.xml index e120686e5..feb2b813f 100644 --- a/samples/browseable/SpeedTracker/Wearable/AndroidManifest.xml +++ b/samples/browseable/SpeedTracker/Wearable/AndroidManifest.xml @@ -24,7 +24,7 @@ <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <application android:allowBackup="true" @@ -32,6 +32,10 @@ android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault"> + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + <!--If you want your app to run on pre-22, then set required to false --> <uses-library android:name="com.google.android.wearable" android:required="false" /> diff --git a/samples/browseable/SpeedTracker/Wearable/src/com.example.android.wearable.speedtracker/WearableMainActivity.java b/samples/browseable/SpeedTracker/Wearable/src/com.example.android.wearable.speedtracker/WearableMainActivity.java index 66550c6c5..20ded86fa 100644 --- a/samples/browseable/SpeedTracker/Wearable/src/com.example.android.wearable.speedtracker/WearableMainActivity.java +++ b/samples/browseable/SpeedTracker/Wearable/src/com.example.android.wearable.speedtracker/WearableMainActivity.java @@ -29,6 +29,7 @@ import com.google.android.gms.wearable.PutDataRequest; import com.google.android.gms.wearable.Wearable; import android.Manifest; +import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; @@ -326,6 +327,7 @@ public class WearableMainActivity extends WearableActivity implements } } + @SuppressLint("MissingPermission") @Override public void onConnected(Bundle bundle) { diff --git a/samples/browseable/SpeedTracker/_index.jd b/samples/browseable/SpeedTracker/_index.jd index e87095247..398e9d4a0 100644 --- a/samples/browseable/SpeedTracker/_index.jd +++ b/samples/browseable/SpeedTracker/_index.jd @@ -1,13 +1,12 @@ -page.tags="Speed Tracker" +page.tags="SpeedTracker" sample.group=Wearable @jd:body <p> This sample uses the FusedLocation APIs of Google Play Services -on those devices that have a hardware GPS built in. In those -cases, this sample provides a simple screen that shows the +to gather location and speed. The sample provides a simple screen that shows the current speed of the device on the watch. User can set a speed limit and if the speed approaches that limit, it changes the color to yellow and if it exceeds the limit, it turns red. User diff --git a/samples/browseable/StorageClient/res/menu/main.xml b/samples/browseable/StorageClient/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/StorageClient/res/menu/main.xml +++ b/samples/browseable/StorageClient/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/StorageProvider/res/menu/main.xml b/samples/browseable/StorageProvider/res/menu/main.xml index 2c3515dd4..498f2c60e 100644 --- a/samples/browseable/StorageProvider/res/menu/main.xml +++ b/samples/browseable/StorageProvider/res/menu/main.xml @@ -14,8 +14,9 @@ limitations under the License. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/sample_action" - android:showAsAction="ifRoom|withText" + app:showAsAction="ifRoom|withText" android:title="@string/sample_action" /> </menu> diff --git a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java index e2b83d355..7bb29c847 100644 --- a/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java +++ b/samples/browseable/SwipeRefreshMultipleViews/src/com.example.android.swiperefreshmultipleviews/SwipeRefreshMultipleViewsFragment.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 The Android Open Source Project + * Copyright 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,22 +87,17 @@ public class SwipeRefreshMultipleViewsFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_sample, container, false); - // Retrieve the SwipeRefreshLayout and GridView instances mSwipeRefreshLayout = (MultiSwipeRefreshLayout) view.findViewById(R.id.swiperefresh); // BEGIN_INCLUDE (change_colors) // Set the color scheme of the SwipeRefreshLayout by providing 4 color resource ids - mSwipeRefreshLayout.setColorScheme( + mSwipeRefreshLayout.setColorSchemeResources( R.color.swipe_color_1, R.color.swipe_color_2, R.color.swipe_color_3, R.color.swipe_color_4); // END_INCLUDE (change_colors) - // Retrieve the GridView mGridView = (GridView) view.findViewById(android.R.id.list); - - // Retrieve the empty view mEmptyView = view.findViewById(android.R.id.empty); - return view; } // END_INCLUDE (inflate_view) @@ -116,7 +111,7 @@ public class SwipeRefreshMultipleViewsFragment extends Fragment { * Create an ArrayAdapter to contain the data for the GridView. Each item in the GridView * uses the system-defined simple_list_item_1 layout that contains one TextView. Initially */ - mListAdapter = new ArrayAdapter<String>( + mListAdapter = new ArrayAdapter<>( getActivity(), android.R.layout.simple_list_item_1, android.R.id.text1); diff --git a/samples/browseable/SynchronizedNotifications/Wearable/AndroidManifest.xml b/samples/browseable/SynchronizedNotifications/Wearable/AndroidManifest.xml index 2bc2fd860..86e8f7a9c 100644 --- a/samples/browseable/SynchronizedNotifications/Wearable/AndroidManifest.xml +++ b/samples/browseable/SynchronizedNotifications/Wearable/AndroidManifest.xml @@ -20,7 +20,7 @@ package="com.example.android.wearable.synchronizednotifications"> <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="22" /> + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -29,6 +29,11 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault.Light" > + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <activity diff --git a/samples/browseable/WatchFace/Application/AndroidManifest.xml b/samples/browseable/WatchFace/Application/AndroidManifest.xml index d946cdb0f..4092097e0 100644 --- a/samples/browseable/WatchFace/Application/AndroidManifest.xml +++ b/samples/browseable/WatchFace/Application/AndroidManifest.xml @@ -18,7 +18,7 @@ package="com.example.android.wearable.watchface" > <uses-sdk android:minSdkVersion="18" - android:targetSdkVersion="23" /> + android:targetSdkVersion="25" /> <!-- Permissions required by the wearable app --> <uses-permission android:name="android.permission.WAKE_LOCK" /> diff --git a/samples/browseable/WatchFace/Application/res/values/base-strings.xml b/samples/browseable/WatchFace/Application/res/values/base-strings.xml index 49e710098..be177c52f 100644 --- a/samples/browseable/WatchFace/Application/res/values/base-strings.xml +++ b/samples/browseable/WatchFace/Application/res/values/base-strings.xml @@ -20,17 +20,32 @@ <string name="intro_message"> <![CDATA[ - + This sample demonstrates how to create watch faces for android wear and includes a phone app and a wearable app. The wearable app has a variety of watch faces including analog, digital, opengl, calendar, steps, interactive, etc. It also includes a watch-side configuration example. The phone app includes a phone-side configuration example. -Additional note on Steps WatchFace Sample, if the user has not installed or setup the Google Fit app -on their phone and their Wear device has not configured the Google Fit Wear App, then you may get -zero steps until one of the two is setup. Please note, many Wear devices configure the Google Fit -Wear App beforehand. - +Because watch face apps do not have a default Activity in their project, you will need to set your +Configurations to "Do not launch Activity" for both the Wear and Application modules. If you are +unsure how to do this, please review the "Run Starter project" section in the +[Google Watch Face Code Lab][1]. + +For the Fit Distance related watch face, authentication IS a requirement to request distance from +Google Fit on Wear. Otherwise, distance will always come back as zero (or stay at whatever the +distance was prior to you de-authorizing the watch face). + +To authenticate and communicate with Google Fit, you must create a project in the Google Developers +Console, activate the Fitness API, create an OAuth 2.0 client ID, and register the public +certificate from your signed APK. More details can be found [here][2]. + +If the user has not installed or setup the Google Fit app on their phone and their Wear device has +not configured the Google Fit Wear App, then you may get zero steps until one of the two is setup. +Please note, many Wear devices configure the Google Fit Wear App beforehand. + +[1]: https://codelabs.developers.google.com/codelabs/watchface/index.html +[2]: https://developers.google.com/fit/android/get-started#step_3_enable_the_fitness_api + ]]> </string> diff --git a/samples/browseable/WatchFace/Wearable/AndroidManifest.xml b/samples/browseable/WatchFace/Wearable/AndroidManifest.xml index 7dd5b69a9..09b914eb2 100644 --- a/samples/browseable/WatchFace/Wearable/AndroidManifest.xml +++ b/samples/browseable/WatchFace/Wearable/AndroidManifest.xml @@ -19,7 +19,7 @@ <uses-sdk android:minSdkVersion="21" - android:targetSdkVersion="23"/> + android:targetSdkVersion="25"/> <uses-feature android:name="android.hardware.type.watch"/> @@ -35,14 +35,19 @@ <!-- Location permission used by FitDistanceWatchFaceService --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> - <android:uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> - <android:uses-permission android:name="android.permission.READ_PHONE_STATE"/> - <android:uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.READ_PHONE_STATE"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> diff --git a/samples/browseable/WatchFace/Wearable/src/com.example.android.wearable.watchface/DigitalWatchFaceService.java b/samples/browseable/WatchFace/Wearable/src/com.example.android.wearable.watchface/DigitalWatchFaceService.java index 0a9eff2fe..f3f97fb55 100644 --- a/samples/browseable/WatchFace/Wearable/src/com.example.android.wearable.watchface/DigitalWatchFaceService.java +++ b/samples/browseable/WatchFace/Wearable/src/com.example.android.wearable.watchface/DigitalWatchFaceService.java @@ -28,6 +28,7 @@ import android.graphics.Typeface; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.support.v4.content.ContextCompat; import android.support.wearable.watchface.CanvasWatchFaceService; import android.support.wearable.watchface.WatchFaceService; import android.support.wearable.watchface.WatchFaceStyle; @@ -200,12 +201,15 @@ public class DigitalWatchFaceService extends CanvasWatchFaceService { mBackgroundPaint = new Paint(); mBackgroundPaint.setColor(mInteractiveBackgroundColor); - mDatePaint = createTextPaint(resources.getColor(R.color.digital_date)); + mDatePaint = createTextPaint( + ContextCompat.getColor(getApplicationContext(), R.color.digital_date)); mHourPaint = createTextPaint(mInteractiveHourDigitsColor, BOLD_TYPEFACE); mMinutePaint = createTextPaint(mInteractiveMinuteDigitsColor); mSecondPaint = createTextPaint(mInteractiveSecondDigitsColor); - mAmPmPaint = createTextPaint(resources.getColor(R.color.digital_am_pm)); - mColonPaint = createTextPaint(resources.getColor(R.color.digital_colons)); + mAmPmPaint = createTextPaint( + ContextCompat.getColor(getApplicationContext(), R.color.digital_am_pm)); + mColonPaint = createTextPaint( + ContextCompat.getColor(getApplicationContext(), R.color.digital_colons)); mCalendar = Calendar.getInstance(); mDate = new Date(); diff --git a/samples/browseable/WatchFace/_index.jd b/samples/browseable/WatchFace/_index.jd index 4367419fa..be2d1aa15 100644 --- a/samples/browseable/WatchFace/_index.jd +++ b/samples/browseable/WatchFace/_index.jd @@ -4,15 +4,30 @@ sample.group=Wearable @jd:body <p> - + This sample demonstrates how to create watch faces for android wear and includes a phone app and a wearable app. The wearable app has a variety of watch faces including analog, digital, opengl, calendar, steps, interactive, etc. It also includes a watch-side configuration example. The phone app includes a phone-side configuration example. -Additional note on Steps WatchFace Sample, if the user has not installed or setup the Google Fit app -on their phone and their Wear device has not configured the Google Fit Wear App, then you may get -zero steps until one of the two is setup. Please note, many Wear devices configure the Google Fit -Wear App beforehand. - +Because watch face apps do not have a default Activity in their project, you will need to set your +Configurations to "Do not launch Activity" for both the Wear and Application modules. If you are +unsure how to do this, please review the "Run Starter project" section in the +[Google Watch Face Code Lab][1]. + +For the Fit Distance related watch face, authentication IS a requirement to request distance from +Google Fit on Wear. Otherwise, distance will always come back as zero (or stay at whatever the +distance was prior to you de-authorizing the watch face). + +To authenticate and communicate with Google Fit, you must create a project in the Google Developers +Console, activate the Fitness API, create an OAuth 2.0 client ID, and register the public +certificate from your signed APK. More details can be found [here][2]. + +If the user has not installed or setup the Google Fit app on their phone and their Wear device has +not configured the Google Fit Wear App, then you may get zero steps until one of the two is setup. +Please note, many Wear devices configure the Google Fit Wear App beforehand. + +[1]: https://codelabs.developers.google.com/codelabs/watchface/index.html +[2]: https://developers.google.com/fit/android/get-started#step_3_enable_the_fitness_api + </p> diff --git a/samples/browseable/WatchViewStub/AndroidManifest.xml b/samples/browseable/WatchViewStub/AndroidManifest.xml index 774817bad..ce1dfc3c6 100644 --- a/samples/browseable/WatchViewStub/AndroidManifest.xml +++ b/samples/browseable/WatchViewStub/AndroidManifest.xml @@ -17,8 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.google.wearable.watchviewstub" > - <uses-sdk android:minSdkVersion="20" - android:targetSdkVersion="22" /> + <uses-sdk android:minSdkVersion="22" + android:targetSdkVersion="25" /> <uses-feature android:name="android.hardware.type.watch" /> @@ -27,6 +27,11 @@ android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <activity android:name="com.example.android.google.wearable.watchviewstub.MainActivity" android:label="@string/app_name"> diff --git a/samples/browseable/WearDrawers/AndroidManifest.xml b/samples/browseable/WearDrawers/AndroidManifest.xml index eaf9dbdbf..fae56dce6 100644 --- a/samples/browseable/WearDrawers/AndroidManifest.xml +++ b/samples/browseable/WearDrawers/AndroidManifest.xml @@ -13,6 +13,10 @@ android:supportsRtl="true" android:theme="@android:style/Theme.DeviceDefault"> + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <!--If you want your app to run on pre-22, then set required to false --> <uses-library android:name="com.google.android.wearable" android:required="false" /> diff --git a/samples/browseable/WearDrawers/src/com.example.android.wearable.wear.weardrawers/MainActivity.java b/samples/browseable/WearDrawers/src/com.example.android.wearable.wear.weardrawers/MainActivity.java index f2e00c056..4fc2f2f70 100644 --- a/samples/browseable/WearDrawers/src/com.example.android.wearable.wear.weardrawers/MainActivity.java +++ b/samples/browseable/WearDrawers/src/com.example.android.wearable.wear.weardrawers/MainActivity.java @@ -27,11 +27,10 @@ import android.support.wearable.view.drawer.WearableNavigationDrawer; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.Toast; @@ -86,17 +85,22 @@ public class MainActivity extends WearableActivity implements (WearableNavigationDrawer) findViewById(R.id.top_navigation_drawer); mWearableNavigationDrawer.setAdapter(new NavigationAdapter(this)); - // Peeks Navigation drawer on the top. - mWearableDrawerLayout.peekDrawer(Gravity.TOP); - // Bottom Action Drawer mWearableActionDrawer = (WearableActionDrawer) findViewById(R.id.bottom_action_drawer); mWearableActionDrawer.setOnMenuItemClickListener(this); - // Peeks action drawer on the bottom. - mWearableDrawerLayout.peekDrawer(Gravity.BOTTOM); + // Temporarily peeks the navigation and action drawers to ensure the user is aware of them. + ViewTreeObserver observer = mWearableDrawerLayout.getViewTreeObserver(); + observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + mWearableDrawerLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + mWearableDrawerLayout.peekDrawer(Gravity.TOP); + mWearableDrawerLayout.peekDrawer(Gravity.BOTTOM); + } + }); /* Action Drawer Tip: If you only have a single action for your Action Drawer, you can use a * (custom) View to peek on top of the content by calling diff --git a/samples/browseable/WearHighBandwidthNetworking/AndroidManifest.xml b/samples/browseable/WearHighBandwidthNetworking/AndroidManifest.xml new file mode 100644 index 000000000..bc25c0ef4 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/AndroidManifest.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<manifest package="com.example.android.wearable.wear.wearhighbandwidthnetworking" + xmlns:android="http://schemas.android.com/apk/res/android" + android:versionCode="1" + android:versionName="1.0"> + + <uses-feature android:name="android.hardware.type.watch" /> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> + <uses-permission android:name="android.permission.INTERNET" /> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + + <activity + android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + </application> +</manifest>
\ No newline at end of file diff --git a/samples/browseable/WearHighBandwidthNetworking/_index.jd b/samples/browseable/WearHighBandwidthNetworking/_index.jd new file mode 100644 index 000000000..2754d2f40 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/_index.jd @@ -0,0 +1,13 @@ + +page.tags="WearHighBandwidthNetworking" +sample.group=Wearable +@jd:body + +<p> + +Sample demonstrates how to determine if a high-bandwidth network is available for use cases that +require a minimum network bandwidth, such as streaming media or downloading large files. In +addition, the sample demonstrates best practices for asking a user to add a new Wi-Fi network for +high-bandwidth network operations if the bandwidth of currently available networks is inadequate. + + </p> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/bg_action_button.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/bg_action_button.xml new file mode 100644 index 000000000..a337ab1dd --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/bg_action_button.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval" /> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_disconnected.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_disconnected.xml new file mode 100644 index 000000000..d93cc1137 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_disconnected.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#000000" + android:pathData="M19.35 10.04C18.67 6.59 15.64 4 12 4c-1.48 0-2.85.43-4.01 1.17l1.46 1.46C10.21 6.23 11.08 6 12 6c3.04 0 5.5 2.46 5.5 5.5v.5H19c1.66 0 3 1.34 3 3 0 1.13-.64 2.11-1.56 2.62l1.45 1.45C23.16 18.16 24 16.68 24 15c0-2.64-2.05-4.78-4.65-4.96zM3 5.27l2.75 2.74C2.56 8.15 0 10.77 0 14c0 3.31 2.69 6 6 6h11.73l2 2L21 20.73 4.27 4 3 5.27zM7.73 10l8 8H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h1.73z" /> + +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_happy.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_happy.xml new file mode 100644 index 000000000..91cd65609 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_happy.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#000000" + android:pathData="M19.35,10.04 C18.67,6.59,15.64,4,12,4 C9.11,4,6.6,5.64,5.35,8.04 +C2.34,8.36,0,10.91,0,14 C0,17.31,2.69,20,6,20 L19,20 C21.76,20,24,17.76,24,15 +C24,12.36,21.95,10.22,19.35,10.04 Z M19,18 L6,18 C3.79,18,2,16.21,2,14 +S3.79,10,6,10 L6.71,10 C7.37,7.69,9.48,6,12,6 C15.04,6,17.5,8.46,17.5,11.5 +L17.5,12 L19,12 C20.66,12,22,13.34,22,15 S20.66,18,19,18 Z" /> + <path + android:strokeColor="#000000" + android:strokeWidth="2" + android:pathData="M6.58994,13.1803 C6.58994,13.1803,8.59173,15.8724,12.011,15.8726 +C15.2788,15.8728,17.3696,13.2502,17.3696,13.2502" /> +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_sad.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_sad.xml new file mode 100644 index 000000000..5b02f00a6 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_cloud_sad.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#000000" + android:pathData="M19.35,10.04 C18.67,6.59,15.64,4,12,4 C9.11,4,6.6,5.64,5.35,8.04 +C2.34,8.36,0,10.91,0,14 C0,17.31,2.69,20,6,20 L19,20 C21.76,20,24,17.76,24,15 +C24,12.36,21.95,10.22,19.35,10.04 Z M19,18 L6,18 C3.79,18,2,16.21,2,14 +S3.79,10,6,10 L6.71,10 C7.37,7.69,9.48,6,12,6 C15.04,6,17.5,8.46,17.5,11.5 +L17.5,12 L19,12 C20.66,12,22,13.34,22,15 S20.66,18,19,18 Z" /> + <path + android:strokeColor="#000000" + android:strokeWidth="2" + android:pathData="M6.58994,15.9661 C6.58994,15.9661,8.56048,12.5553,11.9798,12.5551 +C15.2476,12.5549,17.3696,15.8962,17.3696,15.8962" /> +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_fast_network.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_fast_network.xml new file mode 100644 index 000000000..378c5ea44 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_fast_network.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path android:fillColor="#fff" + android:pathData="M15.9 5c-.17 0-.32.09-.41.23l-.07.15-5.18 11.65c-.16.29-.26.61-.26.96 0 1.11.9 2.01 2.01 2.01.96 0 1.77-.68 1.96-1.59l.01-.03L16.4 5.5c0-.28-.22-.5-.5-.5zM1 9l2 2c2.88-2.88 6.79-4.08 10.53-3.62l1.19-2.68C9.89 3.84 4.74 5.27 1 9zm20 2l2-2c-1.64-1.64-3.55-2.82-5.59-3.57l-.53 2.82c1.5.62 2.9 1.53 4.12 2.75zm-4 4l2-2c-.8-.8-1.7-1.42-2.66-1.89l-.55 2.92c.42.27.83.59 1.21.97zM5 13l2 2c1.13-1.13 2.56-1.79 4.03-2l1.28-2.88c-2.63-.08-5.3.87-7.31 2.88z"/> + +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_no_network.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_no_network.xml new file mode 100644 index 000000000..e8d8ecce7 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_no_network.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path android:fillColor="#fff" + android:pathData="M23.64 7c-.45-.34-4.93-4-11.64-4-1.5 0-2.89.19-4.15.48L18.18 13.8 23.64 7zm-6.6 8.22L3.27 1.44 2 2.72l2.05 2.06C1.91 5.76.59 6.82.36 7l11.63 14.49.01.01.01-.01 3.9-4.86 3.32 3.32 1.27-1.27-3.46-3.46z"/> + +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_wifi_network.xml b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_wifi_network.xml new file mode 100644 index 000000000..c25b99f69 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/drawable/ic_wifi_network.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path android:fillColor="#fff" + android:pathData="M3.53 10.95l8.46 10.54.01.01.01-.01 8.46-10.54C20.04 10.62 16.81 8 12 8c-4.81 0-8.04 2.62-8.47 2.95z"/> + +</vector> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/layout/activity_main.xml b/samples/browseable/WearHighBandwidthNetworking/res/layout/activity_main.xml new file mode 100644 index 000000000..9b716d964 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/layout/activity_main.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + tools:context="com.example.android.wearable.wear.wearhighbandwidthnetworking.MainActivity"> + + <RelativeLayout + android:id="@+id/connectivity_indicator" + android:layout_height="56dp" + android:layout_width="match_parent" + android:paddingTop="12dp" + style="@style/connectivity_indicator"> + + <ImageView + android:id="@+id/connectivity_icon" + android:layout_centerHorizontal="true" + android:src="@drawable/ic_cloud_disconnected" + style="@style/connectivity_indicator_icon" /> + + <TextView + android:id="@+id/connectivity_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/connectivity_icon" + android:layout_centerHorizontal="true" + android:text="@string/network_disconnected" + style="@style/connectivity_indicator_text" /> + + </RelativeLayout> + + <android.support.wearable.view.BoxInsetLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + app:layout_box="left|right"> + + <TextView + android:id="@+id/info_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:text="@string/info_request_network" + android:textAlignment="center" /> + + <RelativeLayout + android:id="@+id/button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/info_text" + android:focusable="true" + android:onClick="onButtonClick"> + + <ImageView + android:id="@+id/button_icon" + android:src="@drawable/ic_fast_network" + style="@style/action_button" /> + + <TextView + android:id="@+id/button_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_toRightOf="@id/button_icon" + android:layout_centerVertical="true" + android:text="@string/button_request_network" /> + + </RelativeLayout> + + <ProgressBar + android:id="@+id/progress_bar" + android:layout_width="100dp" + android:layout_height="100dp" + android:layout_centerInParent="true" + android:indeterminate="true" + android:visibility="gone" /> + + </RelativeLayout> + </android.support.wearable.view.BoxInsetLayout> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/WearHighBandwidthNetworking/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..cde69bccc --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/WearHighBandwidthNetworking/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c133a0cbd --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..bfa42f0e7 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..324e72cdd --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/WearHighBandwidthNetworking/res/values/colors.xml b/samples/browseable/WearHighBandwidthNetworking/res/values/colors.xml new file mode 100644 index 000000000..30bf46cb3 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/values/colors.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <!-- Colors for primary UI elements --> + <item name="primary" type="color">@color/indigo</item> + <item name="primary_dark" type="color">@color/indigo_40b</item> + <item name="primary_accent" type="color">@color/indigo_100b</item> + <item name="window_background" type="color">@color/indigo_15b</item> + + <item name="text_primary" type="color">#CCBBBBBB</item> + <item name="text_dark" type="color">#CC000000</item> + + <item name="white_80a" type="color">#CCFFFFFF</item> + <item name="white_70a" type="color">#B3FFFFFF</item> + <item name="white_60a" type="color">#99FFFFFF</item> + <item name="white_50a" type="color">#80FFFFFF</item> + + <!-- Material Design Indigo 500 palette for wearables --> + <item name="indigo" type="color">#FF3F51B5</item> + <item name="indigo_100b" type="color">#FF8093FF</item> + <item name="indigo_65b" type="color">#FF3A4AA6</item> + <item name="indigo_40b" type="color">#FF242E66</item> + <item name="indigo_30b" type="color">#FF000B4D</item> + <item name="indigo_15b" type="color">#FF000626</item> + + <!-- Alert overlay colors --> + <item name="connectivity_indicator_text" type="color">#9d9d9d</item> + <item name="connectivity_indicator_bg" type="color">#434343</item> +</resources> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/values/dimens.xml b/samples/browseable/WearHighBandwidthNetworking/res/values/dimens.xml new file mode 100644 index 000000000..39aecced5 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/values/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <!-- Generic text dimensions for styles --> + <dimen name="text_large">22sp</dimen> + <dimen name="text_medium">16sp</dimen> + <dimen name="text_small">12sp</dimen> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearHighBandwidthNetworking/res/values/strings.xml b/samples/browseable/WearHighBandwidthNetworking/res/values/strings.xml new file mode 100644 index 000000000..c80585604 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/values/strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <string name="app_name">Wear High Bandwidth Networking</string> + + <string name="network_fast">Network Fast</string> + <string name="network_slow">Network Slow</string> + <string name="network_disconnected">Disconnected</string> + <string name="network_connecting">Connecting</string> + + <string name="info_request_network">To use this sample, ensure that you have removed saved Wi-Fi networks and the device is unplugged</string> + <string name="info_release_network">A high-bandwidth network is now available</string> + <string name="info_add_wifi">No high-bandwidth network is available. You must add a Wi-Fi network to continue</string> + + <string name="button_request_network">Request High-bandwidth Network</string> + <string name="button_release_network">Release High-bandwidth Network</string> + <string name="button_add_wifi">Add Wi-Fi Network</string> +</resources> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/values/styles.xml b/samples/browseable/WearHighBandwidthNetworking/res/values/styles.xml new file mode 100644 index 000000000..97bbcce0b --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/values/styles.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <style name="text_header" parent="@android:style/Widget.TextView"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textSize">@dimen/text_large</item> + </style> + + <style name="text_normal" parent="@android:style/Widget.TextView"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textSize">@dimen/text_medium</item> + </style> + + <style name="text_small" parent="@android:style/Widget.TextView"> + <item name="android:textColor">@color/text_primary</item> + <item name="android:textSize">@dimen/text_small</item> + </style> + + <style name="action_button" parent="@android:style/Widget.ActionButton"> + <item name="android:background">@drawable/bg_action_button</item> + <item name="android:backgroundTint">@color/primary</item> + <item name="android:tint">@color/white</item> + <item name="android:layout_width">40dp</item> + <item name="android:layout_height">40dp</item> + <item name="android:padding">8dp</item> + <item name="android:paddingStart">8dp</item> + <item name="android:paddingEnd">8dp</item> + <item name="android:scaleType">fitXY</item> + </style> + + <style name="connectivity_indicator"> + <item name="android:background">@color/connectivity_indicator_bg</item> + </style> + + <style name="connectivity_indicator_text"> + <item name="android:textColor">@color/connectivity_indicator_text</item> + <item name="android:textSize">14sp</item> + </style> + + <style name="connectivity_indicator_icon"> + <item name="android:tint">@color/connectivity_indicator_text</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">16dp</item> + </style> +</resources> diff --git a/samples/browseable/WearHighBandwidthNetworking/res/values/themes.xml b/samples/browseable/WearHighBandwidthNetworking/res/values/themes.xml new file mode 100644 index 000000000..7f3054242 --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/res/values/themes.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <style name="AppTheme" parent="@android:style/Theme.DeviceDefault"> + <item name="android:colorPrimary">@color/primary</item> + <item name="android:colorPrimaryDark">@color/primary_dark</item> + <item name="android:colorAccent">@color/primary_accent</item> + <item name="android:windowBackground">@color/window_background</item> + </style> +</resources> diff --git a/samples/browseable/WearHighBandwidthNetworking/src/com.example.android.wearable.wear.wearhighbandwidthnetworking/MainActivity.java b/samples/browseable/WearHighBandwidthNetworking/src/com.example.android.wearable.wear.wearhighbandwidthnetworking/MainActivity.java new file mode 100644 index 000000000..b2161ed6e --- /dev/null +++ b/samples/browseable/WearHighBandwidthNetworking/src/com.example.android.wearable.wear.wearhighbandwidthnetworking/MainActivity.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2016 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.example.android.wearable.wear.wearhighbandwidthnetworking; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.concurrent.TimeUnit; + +/** + * This sample demonstrates how to determine if a high-bandwidth network is available for use cases + * that require a minimum network bandwidth, such as streaming media or downloading large files. + * In addition, the sample demonstrates best practices for asking a user to add a new Wi-Fi network + * for high-bandwidth network operations, if currently available networks are inadequate. + */ +public class MainActivity extends Activity { + private static final String LOG_TAG = MainActivity.class.getSimpleName(); + + // Intent action for sending the user directly to the add Wi-Fi network activity. + private static final String ACTION_ADD_NETWORK_SETTINGS = + "com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS"; + + // Message to notify the network request timout handler that too much time has passed. + private static final int MESSAGE_CONNECTIVITY_TIMEOUT = 1; + + // How long the app should wait trying to connect to a sufficient high-bandwidth network before + // asking the user to add a new Wi-Fi network. + private static final long NETWORK_CONNECTIVITY_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); + + // The minimum network bandwidth required by the app for high-bandwidth operations. + private static final int MIN_NETWORK_BANDWIDTH_KBPS = 10000; + + private ConnectivityManager mConnectivityManager; + private ConnectivityManager.NetworkCallback mNetworkCallback; + + // Handler for dealing with network connection timeouts. + private Handler mHandler; + + private ImageView mConnectivityIcon; + private TextView mConnectivityText; + + private View mButton; + private ImageView mButtonIcon; + private TextView mButtonText; + private TextView mInfoText; + private View mProgressBar; + + // Tags added to the button in the UI to detect what operation the user has requested. + // These are required since the app reuses the button for different states of the app/UI. + // See onButtonClick() for how these tags are used. + static final String TAG_REQUEST_NETWORK = "REQUEST_NETWORK"; + static final String TAG_RELEASE_NETWORK = "RELEASE_NETWORK"; + static final String TAG_ADD_WIFI = "ADD_WIFI"; + + // These constants are used by setUiState() to determine what information to display in the UI, + // as this app reuses UI components for the various states of the app, which is dependent on + // the state of the network. + static final int UI_STATE_REQUEST_NETWORK = 1; + static final int UI_STATE_REQUESTING_NETWORK = 2; + static final int UI_STATE_NETWORK_CONNECTED = 3; + static final int UI_STATE_CONNECTION_TIMEOUT = 4; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + mConnectivityIcon = (ImageView) findViewById(R.id.connectivity_icon); + mConnectivityText = (TextView) findViewById(R.id.connectivity_text); + + mProgressBar = findViewById(R.id.progress_bar); + + mButton = findViewById(R.id.button); + mButton.setTag(TAG_REQUEST_NETWORK); + mButtonIcon = (ImageView) findViewById(R.id.button_icon); + mButtonText = (TextView) findViewById(R.id.button_label); + + mInfoText = (TextView) findViewById(R.id.info_text); + + mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_CONNECTIVITY_TIMEOUT: + Log.d(LOG_TAG, "Network connection timeout"); + setUiState(UI_STATE_CONNECTION_TIMEOUT); + unregisterNetworkCallback(); + break; + } + } + }; + } + + @Override + public void onStop() { + releaseHighBandwidthNetwork(); + super.onStop(); + } + + @Override + public void onResume() { + super.onResume(); + + if (isNetworkHighBandwidth()) { + setUiState(UI_STATE_NETWORK_CONNECTED); + } else { + setUiState(UI_STATE_REQUEST_NETWORK); + } + } + + private void unregisterNetworkCallback() { + if (mNetworkCallback != null) { + Log.d(LOG_TAG, "Unregistering network callback"); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mNetworkCallback = null; + } + } + + // Determine if there is a high-bandwidth network exists. Checks both the active + // and bound networks. Returns false if no network is available (low or high-bandwidth). + private boolean isNetworkHighBandwidth() { + Network network = mConnectivityManager.getBoundNetworkForProcess(); + network = network == null ? mConnectivityManager.getActiveNetwork() : network; + if (network == null) { + return false; + } + + // requires android.permission.ACCESS_NETWORK_STATE + int bandwidth = mConnectivityManager + .getNetworkCapabilities(network).getLinkDownstreamBandwidthKbps(); + + if (bandwidth >= MIN_NETWORK_BANDWIDTH_KBPS) { + return true; + } + + return false; + } + + private void requestHighBandwidthNetwork() { + // Before requesting a high-bandwidth network, ensure prior requests are invalidated. + unregisterNetworkCallback(); + + Log.d(LOG_TAG, "Requesting high-bandwidth network"); + + // Requesting an unmetered network may prevent you from connecting to the cellular + // network on the user's watch or phone; however, unless you explicitly ask for permission + // to a access the user's cellular network, you should request an unmetered network. + NetworkRequest request = new NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build(); + + mNetworkCallback = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(final Network network) { + mHandler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT); + + runOnUiThread(new Runnable() { + @Override + public void run() { + // requires android.permission.INTERNET + if (!mConnectivityManager.bindProcessToNetwork(network)) { + Log.e(LOG_TAG, "ConnectivityManager.bindProcessToNetwork()" + + " requires android.permission.INTERNET"); + setUiState(UI_STATE_REQUEST_NETWORK); + } else { + Log.d(LOG_TAG, "Network available"); + setUiState(UI_STATE_NETWORK_CONNECTED); + } + } + }); + } + + @Override + public void onCapabilitiesChanged(Network network, + NetworkCapabilities networkCapabilities) { + runOnUiThread(new Runnable() { + @Override + public void run() { + Log.d(LOG_TAG, "Network capabilities changed"); + } + }); + } + + @Override + public void onLost(Network network) { + Log.d(LOG_TAG, "Network lost"); + + runOnUiThread(new Runnable() { + @Override + public void run() { + setUiState(UI_STATE_REQUEST_NETWORK); + } + }); + } + }; + + // requires android.permission.CHANGE_NETWORK_STATE + mConnectivityManager.requestNetwork(request, mNetworkCallback); + + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT), + NETWORK_CONNECTIVITY_TIMEOUT_MS); + } + + private void releaseHighBandwidthNetwork() { + mConnectivityManager.bindProcessToNetwork(null); + unregisterNetworkCallback(); + } + + private void addWifiNetwork() { + // requires android.permission.CHANGE_WIFI_STATE + startActivity(new Intent(ACTION_ADD_NETWORK_SETTINGS)); + } + + /** + * Click handler for the button in the UI. The view tag is used to determine the specific + * function of the button. + * + * @param view The view that was clicked + */ + public void onButtonClick(View view) { + switch (view.getTag().toString()) { + case TAG_REQUEST_NETWORK: + requestHighBandwidthNetwork(); + setUiState(UI_STATE_REQUESTING_NETWORK); + break; + + case TAG_RELEASE_NETWORK: + releaseHighBandwidthNetwork(); + setUiState(UI_STATE_REQUEST_NETWORK); + break; + + case TAG_ADD_WIFI: + addWifiNetwork(); + break; + } + } + + // Sets the text and icons the connectivity indicator, button, and info text in the app UI, + // which are all reused for the various states of the app and network connectivity. Also, + // will show/hide a progress bar, which is dependent on the state of the network connectivity + // request. + private void setUiState(int uiState) { + switch (uiState) { + case UI_STATE_REQUEST_NETWORK: + if (isNetworkHighBandwidth()) { + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy); + mConnectivityText.setText(R.string.network_fast); + } else { + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad); + mConnectivityText.setText(R.string.network_slow); + } + + mButton.setTag(TAG_REQUEST_NETWORK); + mButtonIcon.setImageResource(R.drawable.ic_fast_network); + mButtonText.setText(R.string.button_request_network); + mInfoText.setText(R.string.info_request_network); + + break; + + case UI_STATE_REQUESTING_NETWORK: + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected); + mConnectivityText.setText(R.string.network_connecting); + + mProgressBar.setVisibility(View.VISIBLE); + mInfoText.setVisibility(View.GONE); + mButton.setVisibility(View.GONE); + + break; + + case UI_STATE_NETWORK_CONNECTED: + if (isNetworkHighBandwidth()) { + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_happy); + mConnectivityText.setText(R.string.network_fast); + } else { + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_sad); + mConnectivityText.setText(R.string.network_slow); + } + + mProgressBar.setVisibility(View.GONE); + mInfoText.setVisibility(View.VISIBLE); + mButton.setVisibility(View.VISIBLE); + + mButton.setTag(TAG_RELEASE_NETWORK); + mButtonIcon.setImageResource(R.drawable.ic_no_network); + mButtonText.setText(R.string.button_release_network); + mInfoText.setText(R.string.info_release_network); + + break; + + case UI_STATE_CONNECTION_TIMEOUT: + mConnectivityIcon.setImageResource(R.drawable.ic_cloud_disconnected); + mConnectivityText.setText(R.string.network_disconnected); + + mProgressBar.setVisibility(View.GONE); + mInfoText.setVisibility(View.VISIBLE); + mButton.setVisibility(View.VISIBLE); + + mButton.setTag(TAG_ADD_WIFI); + mButtonIcon.setImageResource(R.drawable.ic_wifi_network); + mButtonText.setText(R.string.button_add_wifi); + mInfoText.setText(R.string.info_add_wifi); + + break; + } + } +}
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Application/res/values/base-strings.xml b/samples/browseable/WearNotifications/Application/res/values/base-strings.xml index 554428945..e6aee0d87 100644 --- a/samples/browseable/WearNotifications/Application/res/values/base-strings.xml +++ b/samples/browseable/WearNotifications/Application/res/values/base-strings.xml @@ -16,7 +16,7 @@ --> <resources> - <string name="app_name">Wear Notifications</string> + <string name="app_name">WearNotifications</string> <string name="intro_message"> <![CDATA[ diff --git a/samples/browseable/WearNotifications/Wearable/AndroidManifest.xml b/samples/browseable/WearNotifications/Wearable/AndroidManifest.xml index 95176561b..2fdee0d3b 100644 --- a/samples/browseable/WearNotifications/Wearable/AndroidManifest.xml +++ b/samples/browseable/WearNotifications/Wearable/AndroidManifest.xml @@ -1,19 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - 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. ---> - +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.wearable.wear.wearnotifications" android:versionCode="1" @@ -27,7 +27,10 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" - android:theme="@android:style/Theme.DeviceDefault"> + android:theme="@style/AppThemeOverride"> + + <!-- Let's Play Store know this app is standalone. --> + <meta-data android:name="com.google.android.wearable.standalone" android:value="true"/> <!-- Important Note: Usually, you will want to disable bridging if you have a local/native diff --git a/samples/browseable/WearNotifications/Wearable/res/drawable-hdpi/ic_n_white_48dp.png b/samples/browseable/WearNotifications/Wearable/res/drawable-hdpi/ic_n_white_48dp.png Binary files differnew file mode 100644 index 000000000..33eceae3f --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/res/drawable-hdpi/ic_n_white_48dp.png diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_picture_main.xml b/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_picture_main.xml index c52cd05dc..f9de46cc6 100644 --- a/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_picture_main.xml +++ b/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_picture_main.xml @@ -1,28 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_header_end_padding" - android:paddingLeft="@dimen/list_start_padding" - android:paddingRight="@dimen/list_end_padding" - android:paddingTop="@dimen/activity_header_start_padding" + android:paddingBottom="@dimen/activity_notification_details_bottom_padding" + android:paddingLeft="@dimen/activity_notification_details_right_padding" + android:paddingRight="@dimen/activity_notification_details_left_padding" + android:paddingTop="@dimen/activity_notification_details_top_padding" tools:context=".handlers.BigPictureSocialMainActivity"> <TextView diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_text_main.xml b/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_text_main.xml index 946942682..29d9ba6e4 100644 --- a/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_text_main.xml +++ b/samples/browseable/WearNotifications/Wearable/res/layout/activity_big_text_main.xml @@ -1,28 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_header_end_padding" - android:paddingLeft="@dimen/list_start_padding" - android:paddingRight="@dimen/list_end_padding" - android:paddingTop="@dimen/activity_header_start_padding" + android:paddingBottom="@dimen/activity_notification_details_bottom_padding" + android:paddingLeft="@dimen/activity_notification_details_right_padding" + android:paddingRight="@dimen/activity_notification_details_left_padding" + android:paddingTop="@dimen/activity_notification_details_top_padding" tools:context=".handlers.BigTextMainActivity"> <TextView diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/activity_inbox_main.xml b/samples/browseable/WearNotifications/Wearable/res/layout/activity_inbox_main.xml index 3123ecba8..0925af569 100644 --- a/samples/browseable/WearNotifications/Wearable/res/layout/activity_inbox_main.xml +++ b/samples/browseable/WearNotifications/Wearable/res/layout/activity_inbox_main.xml @@ -1,28 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_header_end_padding" - android:paddingLeft="@dimen/list_start_padding" - android:paddingRight="@dimen/list_end_padding" - android:paddingTop="@dimen/activity_header_start_padding" + android:paddingBottom="@dimen/activity_notification_details_bottom_padding" + android:paddingLeft="@dimen/activity_notification_details_right_padding" + android:paddingRight="@dimen/activity_notification_details_left_padding" + android:paddingTop="@dimen/activity_notification_details_top_padding" tools:context=".handlers.InboxMainActivity"> <TextView diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/activity_main.xml b/samples/browseable/WearNotifications/Wearable/res/layout/activity_main.xml index 467b461c9..5dc46fdd4 100644 --- a/samples/browseable/WearNotifications/Wearable/res/layout/activity_main.xml +++ b/samples/browseable/WearNotifications/Wearable/res/layout/activity_main.xml @@ -1,53 +1,40 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2016 The Android Open Source Project - - 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. ---> -<RelativeLayout + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/mainRelativeLayout" + android:id="@+id/mainFrameLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_header_end_padding" - android:paddingLeft="@dimen/list_start_padding" - android:paddingRight="@dimen/list_end_padding" - android:paddingTop="@dimen/activity_header_start_padding" tools:context="com.example.android.wearable.wear.wearnotifications.StandaloneMainActivity"> - <TextView + <android.support.wearable.view.WearableRecyclerView + android:id="@+id/recycler_view" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Pick Notification Style:" - android:id="@+id/spinnerHeadertextView"/> - - <!-- - android:paddingTop="@dimen/activity_vertical_margin" - --> + android:layout_height="match_parent" + android:scrollbars="vertical" /> - <Spinner - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/spinner" - android:layout_below="@+id/spinnerHeadertextView"/> - - <Button + <TextView + android:id="@+id/header_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Launch!" - android:onClick="onClick" - android:layout_below="@+id/spinner" - android:id="@+id/submit"/> - -</RelativeLayout>
\ No newline at end of file + android:textAlignment="center" + android:paddingTop="@dimen/activity_main_text_header_spacing" + android:paddingBottom="@dimen/activity_main_text_header_spacing" + android:background="@color/grey" + android:text="@string/floating_text"/> +</FrameLayout>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/activity_messaging_main.xml b/samples/browseable/WearNotifications/Wearable/res/layout/activity_messaging_main.xml index 0a710d0dd..f33696576 100644 --- a/samples/browseable/WearNotifications/Wearable/res/layout/activity_messaging_main.xml +++ b/samples/browseable/WearNotifications/Wearable/res/layout/activity_messaging_main.xml @@ -1,28 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="@dimen/activity_header_end_padding" - android:paddingLeft="@dimen/list_start_padding" - android:paddingRight="@dimen/list_end_padding" - android:paddingTop="@dimen/activity_header_start_padding" + android:paddingBottom="@dimen/activity_notification_details_bottom_padding" + android:paddingLeft="@dimen/activity_notification_details_right_padding" + android:paddingRight="@dimen/activity_notification_details_left_padding" + android:paddingTop="@dimen/activity_notification_details_top_padding" tools:context=".handlers.MessagingMainActivity"> <TextView diff --git a/samples/browseable/WearNotifications/Wearable/res/layout/recycler_row_item.xml b/samples/browseable/WearNotifications/Wearable/res/layout/recycler_row_item.xml new file mode 100644 index 000000000..60c577ed0 --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/res/layout/recycler_row_item.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingTop="@dimen/recycler_row_padding" + android:paddingBottom="@dimen/recycler_row_padding" + android:orientation="horizontal"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:src="@drawable/ic_n_white_48dp"/> + + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:textSize="@dimen/recycler_row_text_size" + android:id="@+id/textView"/> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/res/values-round/dimens.xml b/samples/browseable/WearNotifications/Wearable/res/values-round/dimens.xml index 0bfb8a0d8..d7a0e57e2 100644 --- a/samples/browseable/WearNotifications/Wearable/res/values-round/dimens.xml +++ b/samples/browseable/WearNotifications/Wearable/res/values-round/dimens.xml @@ -1,22 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <resources> - <dimen name="activity_header_start_padding">36dp</dimen> - <dimen name="activity_header_end_padding">22dp</dimen> - <dimen name="list_start_padding">36dp</dimen> - <dimen name="list_end_padding">22dp</dimen> + <dimen name="recycler_row_text_size">20sp</dimen> + + <dimen name="activity_notification_details_top_padding">36dp</dimen> + <dimen name="activity_notification_details_bottom_padding">22dp</dimen> + <dimen name="activity_notification_details_right_padding">36dp</dimen> + <dimen name="activity_notification_details_left_padding">22dp</dimen> </resources> diff --git a/samples/browseable/WearNotifications/Wearable/res/values-round/strings.xml b/samples/browseable/WearNotifications/Wearable/res/values-round/strings.xml new file mode 100644 index 000000000..b8eb69e45 --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/res/values-round/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <string name="floating_text">Pick\nNotification Style</string> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/res/values/colors.xml b/samples/browseable/WearNotifications/Wearable/res/values/colors.xml index 752b74fc3..e1632c558 100644 --- a/samples/browseable/WearNotifications/Wearable/res/values/colors.xml +++ b/samples/browseable/WearNotifications/Wearable/res/values/colors.xml @@ -1,19 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> diff --git a/samples/browseable/WearNotifications/Wearable/res/values/dimens.xml b/samples/browseable/WearNotifications/Wearable/res/values/dimens.xml index 05a0801db..e6d1996ea 100644 --- a/samples/browseable/WearNotifications/Wearable/res/values/dimens.xml +++ b/samples/browseable/WearNotifications/Wearable/res/values/dimens.xml @@ -1,22 +1,27 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2013 The Android Open Source Project - - 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 + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <dimen name="activity_main_text_header_spacing">5dp</dimen> - http://www.apache.org/licenses/LICENSE-2.0 + <dimen name="recycler_row_padding">5dp</dimen> + <dimen name="recycler_row_text_size">14sp</dimen> - 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. ---> -<resources> - <dimen name="activity_header_start_padding">16dp</dimen> - <dimen name="activity_header_end_padding">16dp</dimen> - <dimen name="list_start_padding">10dp</dimen> - <dimen name="list_end_padding">10dp</dimen> -</resources> + <dimen name="activity_notification_details_top_padding">5dp</dimen> + <dimen name="activity_notification_details_bottom_padding">5dp</dimen> + <dimen name="activity_notification_details_right_padding">5dp</dimen> + <dimen name="activity_notification_details_left_padding">5dp</dimen> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/res/values/strings.xml b/samples/browseable/WearNotifications/Wearable/res/values/strings.xml index ee3edc2d0..8ea45fead 100644 --- a/samples/browseable/WearNotifications/Wearable/res/values/strings.xml +++ b/samples/browseable/WearNotifications/Wearable/res/values/strings.xml @@ -1,18 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- - Copyright 2016 The Android Open Source Project - - 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. ---> + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> <resources> <string name="app_name">Wear Notifications</string> @@ -40,5 +41,6 @@ This Activity would handle the messaging app\'s functionality for a chat(s). \n\nThe notification type for this example was the Messaging Style! </string> + <string name="floating_text">Pick Notification Style</string> </resources>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/res/values/styles.xml b/samples/browseable/WearNotifications/Wearable/res/values/styles.xml new file mode 100644 index 000000000..1d68f7309 --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/res/values/styles.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <style name="AppThemeOverride" parent="Theme.AppCompat.NoActionBar" /> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/Controller.java b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/Controller.java new file mode 100644 index 000000000..ee372bc6b --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/Controller.java @@ -0,0 +1,22 @@ +package com.example.android.wearable.wear.wearnotifications; + +import android.app.Notification; + +/** + * Controller used to instruct main activity to update {@link Notification} based on changes in + * the {@link CustomRecyclerAdapter} (item selected) which is tied to the + * {@link android.support.wearable.view.WearableRecyclerView}. + */ + +public class Controller { + + private StandaloneMainActivity mView; + + Controller(StandaloneMainActivity standaloneMainActivity) { + mView = standaloneMainActivity; + } + + public void itemSelected(String notificationStyleSelected) { + mView.itemSelected(notificationStyleSelected); + } +}
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/CustomRecyclerAdapter.java b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/CustomRecyclerAdapter.java new file mode 100644 index 000000000..6bdcac371 --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/CustomRecyclerAdapter.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.wearable.wear.wearnotifications; + +import android.widget.ImageView; + +import android.support.v4.app.NotificationCompat; +import android.support.wearable.view.WearableRecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * Provides a binding from {@link NotificationCompat.Style} data set to views displayed within the + * {@link WearableRecyclerView}. + */ +public class CustomRecyclerAdapter extends + WearableRecyclerView.Adapter<CustomRecyclerAdapter.ViewHolder> { + + private static final String TAG = "CustomRecyclerAdapter"; + + private String[] mDataSet; + + // Custom Controller used to instruct main activity to update {@link Notification} and/or + // UI for item selected. + private Controller mController; + + /** + * Provides reference to the views for each data item. We don't maintain a reference to the + * {@link ImageView} (representing the icon), because it does not change for each item. We + * wanted to keep the sample simple, but you could add extra code to customize each icon. + */ + public static class ViewHolder extends WearableRecyclerView.ViewHolder { + + private final TextView mTextView; + + public ViewHolder(View view) { + super(view); + mTextView = (TextView) view.findViewById(R.id.textView); + } + + @Override + public String toString() { return (String) mTextView.getText(); } + } + + public CustomRecyclerAdapter(String[] dataSet, Controller controller) { + mDataSet = dataSet; + mController = controller; + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { + View view = LayoutInflater.from(viewGroup.getContext()) + .inflate(R.layout.recycler_row_item, viewGroup, false); + + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder viewHolder, final int position) { + Log.d(TAG, "Element " + position + " set."); + + viewHolder.mTextView.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View view) { + mController.itemSelected(mDataSet[position]); + } + }); + + // Replaces content of view with correct element from data set + viewHolder.mTextView.setText(mDataSet[position]); + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return mDataSet.length; + } +}
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/GlobalNotificationBuilder.java b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/GlobalNotificationBuilder.java index 4023fbb6e..427c7899a 100644 --- a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/GlobalNotificationBuilder.java +++ b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/GlobalNotificationBuilder.java @@ -1,17 +1,17 @@ /* -Copyright 2016 The Android Open Source Project - -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. + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.wearable.wear.wearnotifications; diff --git a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/ScalingOffsettingHelper.java b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/ScalingOffsettingHelper.java new file mode 100644 index 000000000..84ec2fd33 --- /dev/null +++ b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/ScalingOffsettingHelper.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.wearable.wear.wearnotifications; + +import android.support.wearable.view.DefaultOffsettingHelper; +import android.support.wearable.view.WearableRecyclerView; +import android.view.View; + +/** + * Customizes all items (children) in a {@link WearableRecyclerView} to align to left side of + * surface/watch and shrinks each item (child) as you scroll away from it. + */ +public class ScalingOffsettingHelper extends DefaultOffsettingHelper { + + // Max we scale the child View + private static final float MAX_CHILD_SCALE = 0.65f; + + private float mProgressToCenter; + + public ScalingOffsettingHelper() {} + + // Shrinks icons/text and you scroll away + @Override + public void updateChild(View child, WearableRecyclerView parent) { + super.updateChild(child, parent); + + // Figure out % progress from top to bottom + float centerOffset = ((float) child.getHeight() / 2.0f) / (float) parent.getHeight(); + float yRelativeToCenterOffset = (child.getY() / parent.getHeight()) + centerOffset; + + // Normalize for center + mProgressToCenter = Math.abs(0.5f - yRelativeToCenterOffset); + + // Adjust to the maximum scale + mProgressToCenter = Math.min(mProgressToCenter, MAX_CHILD_SCALE); + + child.setScaleX(1 - mProgressToCenter); + child.setScaleY(1 - mProgressToCenter); + } +}
\ No newline at end of file diff --git a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/StandaloneMainActivity.java b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/StandaloneMainActivity.java index a390aca43..95baa6dca 100644 --- a/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/StandaloneMainActivity.java +++ b/samples/browseable/WearNotifications/Wearable/src/com.example.android.wearable.wear.wearnotifications/StandaloneMainActivity.java @@ -1,17 +1,17 @@ /* -Copyright 2016 The Android Open Source Project - -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. + * Copyright (C) 2016 The Android Open Source Project + * + * 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.example.android.wearable.wear.wearnotifications; @@ -30,12 +30,10 @@ import android.support.v4.app.NotificationManagerCompat; import android.support.v4.app.RemoteInput; import android.support.v7.app.NotificationCompat; import android.support.wearable.activity.WearableActivity; +import android.support.wearable.view.WearableRecyclerView; import android.util.Log; import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.RelativeLayout; -import android.widget.Spinner; +import android.widget.FrameLayout; import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialIntentService; import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialMainActivity; @@ -47,34 +45,39 @@ import com.example.android.wearable.wear.wearnotifications.handlers.MessagingMai import com.example.android.wearable.wear.wearnotifications.mock.MockDatabase; /** - * Demonstrates best practice for Notifications created by local standalone Android Wear apps. All - * Notification examples use Notification Styles. + * Demonstrates best practice for {@link NotificationCompat} Notifications created by local + * standalone Android Wear apps. All {@link NotificationCompat} examples use + * {@link NotificationCompat.Style}. */ -public class StandaloneMainActivity extends WearableActivity - implements AdapterView.OnItemSelectedListener { +public class StandaloneMainActivity extends WearableActivity { private static final String TAG = "StandaloneMainActivity"; public static final int NOTIFICATION_ID = 888; - // Used for Notification Style array and switch statement for Spinner selection + /* + * Used to represent each major {@link NotificationCompat.Style} in the + * {@link WearableRecyclerView}. These constants are also used in a switch statement when one + * of the items is selected to create the appropriate {@link Notification}. + */ private static final String BIG_TEXT_STYLE = "BIG_TEXT_STYLE"; private static final String BIG_PICTURE_STYLE = "BIG_PICTURE_STYLE"; private static final String INBOX_STYLE = "INBOX_STYLE"; private static final String MESSAGING_STYLE = "MESSAGING_STYLE"; - // Collection of notification styles to back ArrayAdapter for Spinner + /* + Collection of major {@link NotificationCompat.Style} to create {@link CustomRecyclerAdapter} + for {@link WearableRecyclerView}. + */ private static final String[] NOTIFICATION_STYLES = {BIG_TEXT_STYLE, BIG_PICTURE_STYLE, INBOX_STYLE, MESSAGING_STYLE}; private NotificationManagerCompat mNotificationManagerCompat; - private int mSelectedNotification = 0; - - // RelativeLayout is needed for SnackBars to alert users when Notifications are disabled for app - private RelativeLayout mMainRelativeLayout; - // TODO (jewalker): convert Spinner to WearableRecyclerView - private Spinner mSpinner; + // Needed for {@link SnackBar} to alert users when {@link Notification} are disabled for app. + private FrameLayout mMainFrameLayout; + private WearableRecyclerView mWearableRecyclerView; + private CustomRecyclerAdapter mCustomRecyclerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { @@ -84,53 +87,49 @@ public class StandaloneMainActivity extends WearableActivity setContentView(R.layout.activity_main); setAmbientEnabled(); + mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext()); - mMainRelativeLayout = (RelativeLayout) findViewById(R.id.mainRelativeLayout); - mSpinner = (Spinner) findViewById(R.id.spinner); + mMainFrameLayout = (FrameLayout) findViewById(R.id.mainFrameLayout); + mWearableRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_view); - mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext()); + // Aligns the first and last items on the list vertically centered on the screen. + mWearableRecyclerView.setCenterEdgeItems(true); - // Create an ArrayAdapter using the string array and a default spinner layout - ArrayAdapter<CharSequence> adapter = - new ArrayAdapter( - this, - android.R.layout.simple_spinner_item, - NOTIFICATION_STYLES); - // Specify the layout to use when the list of choices appears - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - // Apply the adapter to the spinner - mSpinner.setAdapter(adapter); - mSpinner.setOnItemSelectedListener(this); + // Customizes scrolling (zoom) and offsets of WearableRecyclerView's items + ScalingOffsettingHelper scalingOffsettingHelper = new ScalingOffsettingHelper(); + mWearableRecyclerView.setOffsettingHelper(scalingOffsettingHelper); - } + // Improves performance because we know changes in content do not change the layout size of + // the RecyclerView. + mWearableRecyclerView.setHasFixedSize(true); - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - Log.d(TAG, "onItemSelected(): position: " + position + " id: " + id); + // Specifies an adapter (see also next example). + mCustomRecyclerAdapter = new CustomRecyclerAdapter( + NOTIFICATION_STYLES, + // Controller passes selected data from the Adapter out to this Activity to trigger + // updates in the UI/Notifications. + new Controller(this)); - mSelectedNotification = position; - } - @Override - public void onNothingSelected(AdapterView<?> parent) { - // Required + mWearableRecyclerView.setAdapter(mCustomRecyclerAdapter); } - public void onClick(View view) { + // Called by WearableRecyclerView when an item is selected (check onCreate() for initialization) + public void itemSelected(String data) { - Log.d(TAG, "onClick()"); + Log.d(TAG, "itemSelected()"); boolean areNotificationsEnabled = mNotificationManagerCompat.areNotificationsEnabled(); - // TODO (jewalker): Verify this is required, can't find way to disable in Wear 2.0. + // If notifications are disabled, allow user to enable. if (!areNotificationsEnabled) { // Because the user took an action to create a notification, we create a prompt to let // the user re-enable notifications for this application again. Snackbar snackbar = Snackbar .make( - mMainRelativeLayout, - "You need to enable notifications for this app", + mMainFrameLayout, + "", // Not enough space for both text and action text Snackbar.LENGTH_LONG) - .setAction("ENABLE", new View.OnClickListener() { + .setAction("Enable Notifications", new View.OnClickListener() { @Override public void onClick(View view) { // Links to this app's notification settings @@ -141,7 +140,7 @@ public class StandaloneMainActivity extends WearableActivity return; } - String notificationStyle = NOTIFICATION_STYLES[mSelectedNotification]; + String notificationStyle = data; switch (notificationStyle) { case BIG_TEXT_STYLE: diff --git a/samples/browseable/WearNotifications/_index.jd b/samples/browseable/WearNotifications/_index.jd index 1a89c1e82..3d774efc6 100644 --- a/samples/browseable/WearNotifications/_index.jd +++ b/samples/browseable/WearNotifications/_index.jd @@ -1,5 +1,5 @@ -page.tags="Wear Notifications" +page.tags="WearNotifications" sample.group=Wearable @jd:body diff --git a/samples/browseable/WearSpeakerSample/AndroidManifest.xml b/samples/browseable/WearSpeakerSample/AndroidManifest.xml index 135d3e080..e8d9a2944 100644 --- a/samples/browseable/WearSpeakerSample/AndroidManifest.xml +++ b/samples/browseable/WearSpeakerSample/AndroidManifest.xml @@ -26,6 +26,11 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@android:style/Theme.DeviceDefault" > + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + <uses-library android:name="com.google.android.wearable" android:required="false" /> <activity android:name=".MainActivity" @@ -37,5 +42,4 @@ </intent-filter> </activity> </application> - -</manifest> +</manifest>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Application/AndroidManifest.xml b/samples/browseable/WearVerifyRemoteApp/Application/AndroidManifest.xml new file mode 100644 index 000000000..af1ea86ba --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.wearable.wear.wearverifyremoteapp" + android:versionCode="1" + android:versionName="1.0"> + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@mipmap/ic_launcher" + android:theme="@style/AppThemeCustom"> + + <activity android:name=".MainMobileActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + </application> +</manifest>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/drawable-hdpi/tile.9.png b/samples/browseable/WearVerifyRemoteApp/Application/res/drawable-hdpi/tile.9.png Binary files differnew file mode 100644 index 000000000..135862883 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/drawable-hdpi/tile.9.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/layout/activity_main.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/layout/activity_main.xml new file mode 100644 index 000000000..d189f84ed --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/layout/activity_main.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + android:orientation="vertical" + tools:context="com.example.android.wearable.wear.wearverifyremoteapp.MainMobileActivity"> + + <TextView + android:id="@+id/information_text_view" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="5"/> + + <Button + android:id="@+id/remote_open_button" + android:layout_width="match_parent" + android:text="Install App on Wear device(s)" + android:layout_height="0dp" + android:layout_weight="1" + android:visibility="invisible"/> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..cde69bccc --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c133a0cbd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..bfa42f0e7 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..324e72cdd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxxhdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..aee44e138 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-dimens.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-dimens.xml new file mode 100644 index 000000000..22074a2bd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-dimens.xml @@ -0,0 +1,24 @@ +<!-- + Copyright 2013 The Android Open Source Project + + 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. + --> + +<resources> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-styles.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-styles.xml new file mode 100644 index 000000000..03d197418 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values-sw600dp/template-styles.xml @@ -0,0 +1,25 @@ +<!-- + Copyright 2013 The Android Open Source Project + + 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. + --> + +<resources> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceLarge</item> + <item name="android:lineSpacingMultiplier">1.2</item> + <item name="android:shadowDy">-6.5</item> + </style> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values-v11/template-styles.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v11/template-styles.xml new file mode 100644 index 000000000..8c1ea66f2 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v11/template-styles.xml @@ -0,0 +1,22 @@ +<!-- + Copyright 2013 The Android Open Source Project + + 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. + --> + +<resources> + + <!-- Activity themes --> + <style name="Theme.Base" parent="android:Theme.Holo.Light" /> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-colors.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-colors.xml new file mode 100644 index 000000000..8b6ec3f85 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-colors.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2013 The Android Open Source Project + + 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. +--> + +<resources> + + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-template-styles.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-template-styles.xml new file mode 100644 index 000000000..c778e4f98 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values-v21/base-template-styles.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2013 The Android Open Source Project + + 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. +--> + +<resources> + + <!-- Activity themes --> + <style name="Theme.Base" parent="android:Theme.Material.Light"> + </style> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/base-strings.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/base-strings.xml new file mode 100644 index 000000000..906f8f44a --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/base-strings.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2013 The Android Open Source Project + + 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. +--> + +<resources> + <string name="app_name">WearVerifyRemoteApp</string> + <string name="intro_message"> + <![CDATA[ + + +Sample demonstrates best practices for checking if the remote version of your app is installed on a +connected device. This enables standalone Android Wear apps to check if the phone app is installed +and the other way around. + + + ]]> + </string> +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/colors.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/colors.xml new file mode 100644 index 000000000..e1632c558 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/colors.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/dimens.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/dimens.xml new file mode 100644 index 000000000..bc0615c75 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/styles.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/styles.xml new file mode 100644 index 000000000..996e5b686 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/styles.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + + <!-- Base application theme. --> + <style name="AppThemeCustom" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + </style> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-dimens.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-dimens.xml new file mode 100644 index 000000000..39e710b5c --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-dimens.xml @@ -0,0 +1,32 @@ +<!-- + Copyright 2013 The Android Open Source Project + + 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. + --> + +<resources> + + <!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> + + <dimen name="margin_tiny">4dp</dimen> + <dimen name="margin_small">8dp</dimen> + <dimen name="margin_medium">16dp</dimen> + <dimen name="margin_large">32dp</dimen> + <dimen name="margin_huge">64dp</dimen> + + <!-- Semantic definitions --> + + <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> + <dimen name="vertical_page_margin">@dimen/margin_medium</dimen> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-styles.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-styles.xml new file mode 100644 index 000000000..6e7d593dd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/template-styles.xml @@ -0,0 +1,42 @@ +<!-- + Copyright 2013 The Android Open Source Project + + 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. + --> + +<resources> + + <!-- Activity themes --> + + <style name="Theme.Base" parent="android:Theme.Light" /> + + <style name="Theme.Sample" parent="Theme.Base" /> + + <style name="AppTheme" parent="Theme.Sample" /> + <!-- Widget styling --> + + <style name="Widget" /> + + <style name="Widget.SampleMessage"> + <item name="android:textAppearance">?android:textAppearanceMedium</item> + <item name="android:lineSpacingMultiplier">1.1</item> + </style> + + <style name="Widget.SampleMessageTile"> + <item name="android:background">@drawable/tile</item> + <item name="android:shadowColor">#7F000000</item> + <item name="android:shadowDy">-3.5</item> + <item name="android:shadowRadius">2</item> + </style> + +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Application/res/values/wear.xml b/samples/browseable/WearVerifyRemoteApp/Application/res/values/wear.xml new file mode 100644 index 000000000..bcaf684f7 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/res/values/wear.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <string-array name="android_wear_capabilities"> + <!-- IMPORTANT NOTE: Should be different than capability in Wear res/values/wear.xml. --> + <item>verify_remote_example_phone_app</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Application/src/com.example.android.wearable.wear.wearverifyremoteapp/MainMobileActivity.java b/samples/browseable/WearVerifyRemoteApp/Application/src/com.example.android.wearable.wear.wearverifyremoteapp/MainMobileActivity.java new file mode 100644 index 000000000..ed1511936 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Application/src/com.example.android.wearable.wear.wearverifyremoteapp/MainMobileActivity.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2016 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.example.android.wearable.wear.wearverifyremoteapp; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.ResultReceiver; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.wearable.CapabilityApi; +import com.google.android.gms.wearable.CapabilityInfo; +import com.google.android.gms.wearable.Node; +import com.google.android.gms.wearable.NodeApi; +import com.google.android.gms.wearable.Wearable; +import com.google.android.wearable.intent.RemoteIntent; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Checks if the sample's Wear app is installed on remote Wear device(s). If it is not, allows the + * user to open the app listing on the Wear devices' Play Store. + */ +public class MainMobileActivity extends AppCompatActivity implements + GoogleApiClient.ConnectionCallbacks, + GoogleApiClient.OnConnectionFailedListener, + CapabilityApi.CapabilityListener { + + private static final String TAG = "MainMobileActivity"; + + private static final String WELCOME_MESSAGE = "Welcome to our Mobile app!\n\n"; + + private static final String CHECKING_MESSAGE = + WELCOME_MESSAGE + "Checking for Wear Devices for app...\n"; + + private static final String NO_DEVICES = + WELCOME_MESSAGE + + "You have no Wear devices linked to your phone at this time.\n"; + + private static final String MISSING_ALL_MESSAGE = + WELCOME_MESSAGE + + "You are missing the Wear app on all your Wear Devices, please click on the " + + "button below to install it on those device(s).\n"; + + private static final String INSTALLED_SOME_DEVICES_MESSAGE = + WELCOME_MESSAGE + + "Wear app installed on some your device(s) (%s)!\n\nYou can now use the " + + "MessageApi, DataApi, etc.\n\n" + + "To install the Wear app on the other devices, please click on the button " + + "below.\n"; + + private static final String INSTALLED_ALL_DEVICES_MESSAGE = + WELCOME_MESSAGE + + "Wear app installed on all your devices (%s)!\n\nYou can now use the " + + "MessageApi, DataApi, etc."; + + // Name of capability listed in Wear app's wear.xml. + // IMPORTANT NOTE: This should be named differently than your Phone app's capability. + private static final String CAPABILITY_WEAR_APP = "verify_remote_example_wear_app"; + + // Links to Wear app (Play Store). + // TODO: Replace with your links/packages. + private static final String PLAY_STORE_APP_URI = + "market://details?id=com.example.android.wearable.wear.wearverifyremoteapp"; + + // Result from sending RemoteIntent to wear device(s) to open app in play/app store. + private final ResultReceiver mResultReceiver = new ResultReceiver(new Handler()) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + Log.d(TAG, "onReceiveResult: " + resultCode); + + if (resultCode == RemoteIntent.RESULT_OK) { + Toast toast = Toast.makeText( + getApplicationContext(), + "Play Store Request to Wear device successful.", + Toast.LENGTH_SHORT); + toast.show(); + + } else if (resultCode == RemoteIntent.RESULT_FAILED) { + Toast toast = Toast.makeText( + getApplicationContext(), + "Play Store Request Failed. Wear device(s) may not support Play Store, " + + " that is, the Wear device may be version 1.0.", + Toast.LENGTH_LONG); + toast.show(); + + } else { + throw new IllegalStateException("Unexpected result " + resultCode); + } + } + }; + + private TextView mInformationTextView; + private Button mRemoteOpenButton; + + private Set<Node> mWearNodesWithApp; + private List<Node> mAllConnectedNodes; + + private GoogleApiClient mGoogleApiClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mInformationTextView = (TextView) findViewById(R.id.information_text_view); + mRemoteOpenButton = (Button) findViewById(R.id.remote_open_button); + + mInformationTextView.setText(CHECKING_MESSAGE); + + mRemoteOpenButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openPlayStoreOnWearDevicesWithoutApp(); + } + }); + + mGoogleApiClient = new GoogleApiClient.Builder(this) + .addApi(Wearable.API) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .build(); + } + + + @Override + protected void onPause() { + Log.d(TAG, "onPause()"); + super.onPause(); + + if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) { + + Wearable.CapabilityApi.removeCapabilityListener( + mGoogleApiClient, + this, + CAPABILITY_WEAR_APP); + + mGoogleApiClient.disconnect(); + } + } + + @Override + protected void onResume() { + Log.d(TAG, "onResume()"); + super.onResume(); + if (mGoogleApiClient != null) { + mGoogleApiClient.connect(); + } + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + Log.d(TAG, "onConnected()"); + + // Set up listeners for capability changes (install/uninstall of remote app). + Wearable.CapabilityApi.addCapabilityListener( + mGoogleApiClient, + this, + CAPABILITY_WEAR_APP); + + // Initial request for devices with our capability, aka, our Wear app installed. + findWearDevicesWithApp(); + + // Initial request for all Wear devices connected (with or without our capability). + // Additional Note: Because there isn't a listener for ALL Nodes added/removed from network + // that isn't deprecated, we simply update the full list when the Google API Client is + // connected and when capability changes come through in the onCapabilityChanged() method. + findAllWearDevices(); + } + + @Override + public void onConnectionSuspended(int i) { + Log.d(TAG, "onConnectionSuspended(): connection to location client suspended: " + i); + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Log.e(TAG, "onConnectionFailed(): " + connectionResult); + } + + /* + * Updates UI when capabilities change (install/uninstall wear app). + */ + public void onCapabilityChanged(CapabilityInfo capabilityInfo) { + Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo); + + mWearNodesWithApp = capabilityInfo.getNodes(); + + // Because we have an updated list of devices with/without our app, we need to also update + // our list of active Wear devices. + findAllWearDevices(); + + verifyNodeAndUpdateUI(); + } + + private void findWearDevicesWithApp() { + Log.d(TAG, "findWearDevicesWithApp()"); + + // You can filter this by FILTER_REACHABLE if you only want to open Nodes (Wear Devices) + // directly connect to your phone. + PendingResult<CapabilityApi.GetCapabilityResult> pendingResult = + Wearable.CapabilityApi.getCapability( + mGoogleApiClient, + CAPABILITY_WEAR_APP, + CapabilityApi.FILTER_ALL); + + pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() { + @Override + public void onResult(@NonNull CapabilityApi.GetCapabilityResult getCapabilityResult) { + Log.d(TAG, "onResult(): " + getCapabilityResult); + + if (getCapabilityResult.getStatus().isSuccess()) { + CapabilityInfo capabilityInfo = getCapabilityResult.getCapability(); + mWearNodesWithApp = capabilityInfo.getNodes(); + verifyNodeAndUpdateUI(); + + } else { + Log.d(TAG, "Failed CapabilityApi: " + getCapabilityResult.getStatus()); + } + } + }); + } + + private void findAllWearDevices() { + Log.d(TAG, "findAllWearDevices()"); + + PendingResult<NodeApi.GetConnectedNodesResult> pendingResult = + Wearable.NodeApi.getConnectedNodes(mGoogleApiClient); + + pendingResult.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() { + @Override + public void onResult(@NonNull NodeApi.GetConnectedNodesResult getConnectedNodesResult) { + + if (getConnectedNodesResult.getStatus().isSuccess()) { + mAllConnectedNodes = getConnectedNodesResult.getNodes(); + verifyNodeAndUpdateUI(); + + } else { + Log.d(TAG, "Failed CapabilityApi: " + getConnectedNodesResult.getStatus()); + } + } + }); + } + + private void verifyNodeAndUpdateUI() { + Log.d(TAG, "verifyNodeAndUpdateUI()"); + + if ((mWearNodesWithApp == null) || (mAllConnectedNodes == null)) { + Log.d(TAG, "Waiting on Results for both connected nodes and nodes with app"); + + } else if (mAllConnectedNodes.isEmpty()) { + Log.d(TAG, NO_DEVICES); + mInformationTextView.setText(NO_DEVICES); + mRemoteOpenButton.setVisibility(View.INVISIBLE); + + } else if (mWearNodesWithApp.isEmpty()) { + Log.d(TAG, MISSING_ALL_MESSAGE); + mInformationTextView.setText(MISSING_ALL_MESSAGE); + mRemoteOpenButton.setVisibility(View.VISIBLE); + + } else if (mWearNodesWithApp.size() < mAllConnectedNodes.size()) { + // TODO: Add your code to communicate with the wear app(s) via + // Wear APIs (MessageApi, DataApi, etc.) + + String installMessage = + String.format(INSTALLED_SOME_DEVICES_MESSAGE, mWearNodesWithApp); + Log.d(TAG, installMessage); + mInformationTextView.setText(installMessage); + mRemoteOpenButton.setVisibility(View.VISIBLE); + + } else { + // TODO: Add your code to communicate with the wear app(s) via + // Wear APIs (MessageApi, DataApi, etc.) + + String installMessage = + String.format(INSTALLED_ALL_DEVICES_MESSAGE, mWearNodesWithApp); + Log.d(TAG, installMessage); + mInformationTextView.setText(installMessage); + mRemoteOpenButton.setVisibility(View.INVISIBLE); + + } + } + + private void openPlayStoreOnWearDevicesWithoutApp() { + Log.d(TAG, "openPlayStoreOnWearDevicesWithoutApp()"); + + // Create a List of Nodes (Wear devices) without your app. + ArrayList<Node> nodesWithoutApp = new ArrayList<>(); + + for (Node node : mAllConnectedNodes) { + if (!mWearNodesWithApp.contains(node)) { + nodesWithoutApp.add(node); + } + } + + if (!nodesWithoutApp.isEmpty()) { + Log.d(TAG, "Number of nodes without app: " + nodesWithoutApp.size()); + + Intent intent = + new Intent(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(Uri.parse(PLAY_STORE_APP_URI)); + + for (Node node : nodesWithoutApp) { + RemoteIntent.startRemoteActivity( + getApplicationContext(), + intent, + mResultReceiver, + node.getId()); + } + } + } +}
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/AndroidManifest.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/AndroidManifest.xml new file mode 100644 index 000000000..ee3f2dc11 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<manifest package="com.example.android.wearable.wear.wearverifyremoteapp" + xmlns:android="http://schemas.android.com/apk/res/android" + android:versionCode="1" + android:versionName="1.0"> + + <uses-feature android:name="android.hardware.type.watch"/> + <!-- Required for Always-on. --> + <uses-permission android:name="android.permission.WAKE_LOCK"/> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@android:style/Theme.DeviceDefault"> + + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="true" /> + + <!--If you want your app to run on pre-22, then set required to false --> + <uses-library android:name="com.google.android.wearable" android:required="false" /> + + <activity + android:name=".MainWearActivity" + android:label="@string/app_name" + android:theme="@android:style/Theme.DeviceDefault.Light"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + </application> +</manifest>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/layout/activity_main.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/res/layout/activity_main.xml new file mode 100644 index 000000000..3a997745b --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/layout/activity_main.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingTop="@dimen/activity_main_top_padding" + android:paddingRight="@dimen/activity_main_right_padding" + android:paddingLeft="@dimen/activity_main_left_padding" + android:paddingBottom="@dimen/activity_main_bottom_padding" + tools:context="com.example.android.wearable.wear.wearverifyremoteapp.MainWearActivity"> + + <TextView + android:id="@+id/information_text_view" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="3"/> + + <Button + android:id="@+id/remote_open_button" + android:layout_width="match_parent" + android:text="Install Mobile App" + android:layout_height="0dp" + android:layout_weight="1" + android:visibility="invisible"/> +</LinearLayout>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-hdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..cde69bccc --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-mdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..c133a0cbd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xhdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..bfa42f0e7 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xxhdpi/ic_launcher.png b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..324e72cdd --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/values-round/dimens.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values-round/dimens.xml new file mode 100644 index 000000000..b0152cd7e --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values-round/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <dimen name="activity_main_top_padding">36dp</dimen> + <dimen name="activity_main_bottom_padding">22dp</dimen> + <dimen name="activity_main_right_padding">36dp</dimen> + <dimen name="activity_main_left_padding">36dp</dimen> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/dimens.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/dimens.xml new file mode 100644 index 000000000..cacdaf3f5 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <dimen name="activity_main_top_padding">5dp</dimen> + <dimen name="activity_main_bottom_padding">5dp</dimen> + <dimen name="activity_main_right_padding">5dp</dimen> + <dimen name="activity_main_left_padding">5dp</dimen> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/strings.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/strings.xml new file mode 100644 index 000000000..65c4ea573 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <string name="app_name">Wear Verify Remote App</string> +</resources> diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/wear.xml b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/wear.xml new file mode 100644 index 000000000..584747ba7 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/res/values/wear.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Google Inc. + ~ + ~ 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. + --> +<resources> + <string-array name="android_wear_capabilities"> + <!-- IMPORTANT NOTE: Should be different than capability in App res/values/wear.xml. --> + <item>verify_remote_example_wear_app</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/Wearable/src/com.example.android.wearable.wear.wearverifyremoteapp/MainWearActivity.java b/samples/browseable/WearVerifyRemoteApp/Wearable/src/com.example.android.wearable.wear.wearverifyremoteapp/MainWearActivity.java new file mode 100644 index 000000000..31882b0b1 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/Wearable/src/com.example.android.wearable.wear.wearverifyremoteapp/MainWearActivity.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2016 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.example.android.wearable.wear.wearverifyremoteapp; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.ResultReceiver; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.wearable.activity.WearableActivity; +import android.support.wearable.view.ConfirmationOverlay; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.wearable.CapabilityApi; +import com.google.android.gms.wearable.CapabilityInfo; +import com.google.android.gms.wearable.Node; +import com.google.android.gms.wearable.Wearable; +import com.google.android.wearable.intent.RemoteIntent; +import com.google.android.wearable.playstore.PlayStoreAvailability; + +import java.util.Set; + +/** + * Checks if the phone app is installed on remote device. If it is not, allows user to open app + * listing on the phone's Play or App Store. + */ +public class MainWearActivity extends WearableActivity implements + GoogleApiClient.ConnectionCallbacks, + GoogleApiClient.OnConnectionFailedListener, + CapabilityApi.CapabilityListener { + + private static final String TAG = "MainWearActivity"; + + private static final String WELCOME_MESSAGE = "Welcome to our Wear app!\n\n"; + + private static final String CHECKING_MESSAGE = + WELCOME_MESSAGE + "Checking for Mobile app...\n"; + + private static final String MISSING_MESSAGE = + WELCOME_MESSAGE + + "You are missing the required phone app, please click on the button below to " + + "install it on your phone.\n"; + + private static final String INSTALLED_MESSAGE = + WELCOME_MESSAGE + + "Mobile app installed on your %s!\n\nYou can now use MessageApi, " + + "DataApi, etc."; + + // Name of capability listed in Phone app's wear.xml. + // IMPORTANT NOTE: This should be named differently than your Wear app's capability. + private static final String CAPABILITY_PHONE_APP = "verify_remote_example_phone_app"; + + // Links to install mobile app for both Android (Play Store) and iOS. + // TODO: Replace with your links/packages. + private static final String PLAY_STORE_APP_URI = + "market://details?id=com.example.android.wearable.wear.wearverifyremoteapp"; + + // TODO: Replace with your links/packages. + private static final String APP_STORE_APP_URI = + "https://itunes.apple.com/us/app/android-wear/id986496028?mt=8"; + + // Result from sending RemoteIntent to phone to open app in play/app store. + private final ResultReceiver mResultReceiver = new ResultReceiver(new Handler()) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + + if (resultCode == RemoteIntent.RESULT_OK) { + new ConfirmationOverlay().showOn(MainWearActivity.this); + + } else if (resultCode == RemoteIntent.RESULT_FAILED) { + new ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .showOn(MainWearActivity.this); + + } else { + throw new IllegalStateException("Unexpected result " + resultCode); + } + } + }; + + private TextView mInformationTextView; + private Button mRemoteOpenButton; + + private Node mAndroidPhoneNodeWithApp; + + private GoogleApiClient mGoogleApiClient; + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate()"); + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + setAmbientEnabled(); + + mInformationTextView = (TextView) findViewById(R.id.information_text_view); + mRemoteOpenButton = (Button) findViewById(R.id.remote_open_button); + + mInformationTextView.setText(CHECKING_MESSAGE); + + mRemoteOpenButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openAppInStoreOnPhone(); + } + }); + + mGoogleApiClient = new GoogleApiClient.Builder(this) + .addApi(Wearable.API) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .build(); + } + + + @Override + protected void onPause() { + Log.d(TAG, "onPause()"); + super.onPause(); + + if ((mGoogleApiClient != null) && mGoogleApiClient.isConnected()) { + Wearable.CapabilityApi.removeCapabilityListener( + mGoogleApiClient, + this, + CAPABILITY_PHONE_APP); + + mGoogleApiClient.disconnect(); + } + } + + @Override + protected void onResume() { + Log.d(TAG, "onResume()"); + super.onResume(); + if (mGoogleApiClient != null) { + mGoogleApiClient.connect(); + } + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + Log.d(TAG, "onConnected()"); + + // Set up listeners for capability changes (install/uninstall of remote app). + Wearable.CapabilityApi.addCapabilityListener( + mGoogleApiClient, + this, + CAPABILITY_PHONE_APP); + + checkIfPhoneHasApp(); + } + + @Override + public void onConnectionSuspended(int i) { + Log.d(TAG, "onConnectionSuspended(): connection to location client suspended: " + i); + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Log.e(TAG, "onConnectionFailed(): " + connectionResult); + } + + /* + * Updates UI when capabilities change (install/uninstall phone app). + */ + public void onCapabilityChanged(CapabilityInfo capabilityInfo) { + Log.d(TAG, "onCapabilityChanged(): " + capabilityInfo); + + mAndroidPhoneNodeWithApp = pickBestNodeId(capabilityInfo.getNodes()); + verifyNodeAndUpdateUI(); + } + + private void checkIfPhoneHasApp() { + Log.d(TAG, "checkIfPhoneHasApp()"); + + PendingResult<CapabilityApi.GetCapabilityResult> pendingResult = + Wearable.CapabilityApi.getCapability( + mGoogleApiClient, + CAPABILITY_PHONE_APP, + CapabilityApi.FILTER_ALL); + + pendingResult.setResultCallback(new ResultCallback<CapabilityApi.GetCapabilityResult>() { + + @Override + public void onResult(@NonNull CapabilityApi.GetCapabilityResult getCapabilityResult) { + Log.d(TAG, "onResult(): " + getCapabilityResult); + + if (getCapabilityResult.getStatus().isSuccess()) { + CapabilityInfo capabilityInfo = getCapabilityResult.getCapability(); + mAndroidPhoneNodeWithApp = pickBestNodeId(capabilityInfo.getNodes()); + verifyNodeAndUpdateUI(); + + } else { + Log.d(TAG, "Failed CapabilityApi: " + getCapabilityResult.getStatus()); + } + } + }); + } + + private void verifyNodeAndUpdateUI() { + + if (mAndroidPhoneNodeWithApp != null) { + + // TODO: Add your code to communicate with the phone app via + // Wear APIs (MessageApi, DataApi, etc.) + + String installMessage = + String.format(INSTALLED_MESSAGE, mAndroidPhoneNodeWithApp.getDisplayName()); + Log.d(TAG, installMessage); + mInformationTextView.setText(installMessage); + mRemoteOpenButton.setVisibility(View.INVISIBLE); + + } else { + Log.d(TAG, MISSING_MESSAGE); + mInformationTextView.setText(MISSING_MESSAGE); + mRemoteOpenButton.setVisibility(View.VISIBLE); + } + } + + private void openAppInStoreOnPhone() { + Log.d(TAG, "openAppInStoreOnPhone()"); + + int playStoreAvailabilityOnPhone = + PlayStoreAvailability.getPlayStoreAvailabilityOnPhone(getApplicationContext()); + + switch (playStoreAvailabilityOnPhone) { + + // Android phone with the Play Store. + case PlayStoreAvailability.PLAY_STORE_ON_PHONE_AVAILABLE: + Log.d(TAG, "\tPLAY_STORE_ON_PHONE_AVAILABLE"); + + // Create Remote Intent to open Play Store listing of app on remote device. + Intent intentAndroid = + new Intent(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(Uri.parse(PLAY_STORE_APP_URI)); + + RemoteIntent.startRemoteActivity( + getApplicationContext(), + intentAndroid, + mResultReceiver); + break; + + // Assume iPhone (iOS device) or Android without Play Store (not supported right now). + case PlayStoreAvailability.PLAY_STORE_ON_PHONE_UNAVAILABLE: + Log.d(TAG, "\tPLAY_STORE_ON_PHONE_UNAVAILABLE"); + + // Create Remote Intent to open App Store listing of app on iPhone. + Intent intentIOS = + new Intent(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(Uri.parse(APP_STORE_APP_URI)); + + RemoteIntent.startRemoteActivity( + getApplicationContext(), + intentIOS, + mResultReceiver); + break; + + case PlayStoreAvailability.PLAY_STORE_ON_PHONE_ERROR_UNKNOWN: + Log.d(TAG, "\tPLAY_STORE_ON_PHONE_ERROR_UNKNOWN"); + break; + } + } + + /* + * There should only ever be one phone in a node set (much less w/ the correct capability), so + * I am just grabbing the first one (which should be the only one). + */ + private Node pickBestNodeId(Set<Node> nodes) { + Log.d(TAG, "pickBestNodeId(): " + nodes); + + Node bestNodeId = null; + // Find a nearby node/phone or pick one arbitrarily. Realistically, there is only one phone. + for (Node node : nodes) { + bestNodeId = node; + } + return bestNodeId; + } +}
\ No newline at end of file diff --git a/samples/browseable/WearVerifyRemoteApp/_index.jd b/samples/browseable/WearVerifyRemoteApp/_index.jd new file mode 100644 index 000000000..f9fb5f861 --- /dev/null +++ b/samples/browseable/WearVerifyRemoteApp/_index.jd @@ -0,0 +1,12 @@ + +page.tags="WearVerifyRemoteApp" +sample.group=Wearable +@jd:body + +<p> + +Sample demonstrates best practices for checking if the remote version of your app is installed on a +connected device. This enables standalone Android Wear apps to check if the phone app is installed +and the other way around. + + </p> diff --git a/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml b/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml index fc086da1a..320396db5 100644 --- a/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml +++ b/samples/browseable/XYZTouristAttractions/Wearable/AndroidManifest.xml @@ -25,8 +25,8 @@ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" /> <uses-sdk - android:minSdkVersion="21" - android:targetSdkVersion="22" /> + android:minSdkVersion="22" + android:targetSdkVersion="25" /> <application android:allowBackup="true" @@ -34,6 +34,10 @@ android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault" > + <meta-data + android:name="com.google.android.wearable.standalone" + android:value="false" /> + <activity android:name=".ui.AttractionsActivity" android:label="@string/app_name" /> diff --git a/samples/samples_source.prop_template b/samples/samples_source.prop_template index d3cdfd5ee..523d6bda8 100644 --- a/samples/samples_source.prop_template +++ b/samples/samples_source.prop_template @@ -1,4 +1,4 @@ Pkg.UserSrc=false -Pkg.Revision=1 +Pkg.Revision=2 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION} AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME} diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml index d475889f1..4a1868ad4 100644 --- a/testrunner/test_defs.xml +++ b/testrunner/test_defs.xml @@ -71,6 +71,7 @@ See test_defs.xsd for more information. <test name="frameworks-telephony" build_path="frameworks/opt/telephony/tests/telephonytests" package="com.android.frameworks.telephonytests" + runner="android.support.test.runner.AndroidJUnitRunner" coverage_target="framework" continuous="true" /> |