Android Design Support Library 22.2 and Play Services 7.5

LYK dalinaum@gmail.com

About me

Leonardo YongUk Kim

  • Organizer of GDG Korea Android
  • dalinaum@gmail.com

Android Design Support Library
Play Services

Android Design Support Library

Android Design Support Lib

  • <sdk>/extras/android/support/design
  • compile 'com.android.support:design:22.2.0'
    • in build.gradle
  • Navigation View
  • Floating labels for editing text
  • Floating Action Button
  • Snackbar
  • Tabs
  • CoordinatorLayout
  • Collapsing Toolbars

This presentation is based on following CodeLab.

http://inthecheesefactory.com/blog/android-design-support-library-codelab/en

Floating Action Button

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fabBtn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:src="@drawable/ic_plus"
    app:fabSize="normal" />

Snack Bar

Snackbar.make(someView, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
        .setAction("Undo", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
            }
        })
        .show();

TabLayout

<android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
tabLayout = (TabLayout) findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));

CoordinatorLayout

let children work coordinated

let children work coordinated?

There is no magic.

should wrap every coordinated childrend

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <android.support.design.widget.FloatingActionButton
        ... />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.CoordinatorLayout
    ...>
 
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
       <android.support.v7.widget.Toolbar
           .../>
    </android.support.design.widget.AppBarLayout>
 
    <android.support.design.widget.FloatingActionButton
        ...>
    </android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>

NestedScrollView

ScrollView doesn't work with CoordinatorLayout well.

<android.support.design.widget.CoordinatorLayout
    ...>

    <android.support.design.widget.AppBarLayout
        ...>
    </android.support.design.widget.AppBarLayout>

    <android.support.design.widget.TabLayout
       ...
    app:layout_scrollFlags="scroll|enterAlways" />

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.widget.NestedScrollView ...>
    <LinearLayout ...>
        ...
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.TabLayout
    ...
    app:layout_scrollFlags="scroll|enterAlways" />

ListView doesn't work with CoordinatorLayout well.

KEEP
CALM

and

USE
RECYCLE

VIEW

CollapsingToolbar Layout

Toolbar in CollapsingToolbarLayout in AppBarLayout

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="256dp">
 
    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsingToolbarLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">
 
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
 
    </android.support.design.widget.CollapsingToolbarLayout>
 
</android.support.design.widget.AppBarLayout>

Play Services 7.5

Smart Lock for Passwords

Auth.CredentialsApi.save(mCredentialsClient, credential).setResultCallback(
  new ResultCallback() {
      @Override
      public void onResult(Status status) {
          if (status.isSuccess()) {
              // Credentials were saved
          } else {
              if (status.hasResolution()) {
                  // Try to resolve the save request. This will prompt the user if
                  // the credential is new.
                  try {
                      status.startResolutionForResult(this, RC_SAVE);
                  } catch (IntentSender.SendIntentException e) {
                      // Could not resolve the request
                  }
              }
          }
      }
  });
Auth.CredentialsApi.request(mCredentialsClient, mCredentialRequest).setResultCallback(
  new ResultCallback() {
      @Override
      public void onResult(CredentialRequestResult credentialRequestResult) {
          if (credentialRequestResult.getStatus().isSuccess()) {
              // Handle successful credential requests
          } else {
              // Handle unsuccessful and incomplete credential requests
          }
      }
  });

Cloud Messaging

GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context);
String GCM_SENDER_ID = "Your-Sender-ID";
AtomicInteger msgId = new AtomicInteger();
String id = Integer.toString(msgId.incrementAndGet());
Bundle data = new Bundle();
// Bundle data consists of a key-value pair
data.putString("hello", "world");
// "time to live" parameter
// This is optional. It specifies a value in seconds up to 24 hours.
int ttl = 86400; // 24 hours;

gcm.send(GCM_SENDER_ID + "@gcm.googleapis.com", id, ttl, data);

Google Maps Lite

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:name="com.google.android.gms.maps.MapFragment"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    map:cameraZoom="13"
    map:mapType="normal"
    map:liteMode="true"/>

Maps for Wear

<FrameLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_height="match_parent"
   android:layout_width="match_parent">

  ...

  <android.support.wearable.view.DismissOverlayView
     android:id="@+id/dismiss_overlay"
     android:layout_height="match_parent"
     android:layout_width="match_parent"/>

</FrameLayout>
public class BasicWearDemoActivity extends FragmentActivity
        implements OnMapReadyCallback, OnMapClickListener,
        OnMapLongClickListener {

    private DismissOverlayView mDismissOverlay;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.basic_wear_demo);

        mDismissOverlay =
            (DismissOverlayView) findViewById(R.id.dismiss_overlay);
        mDismissOverlay.setIntroText(R.string.basic_wear_long_press_intro);
        mDismissOverlay.showIntroIfNecessary();

        SupportMapFragment mapFragment =
                (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }
    ...
}
public void onMapLongClick(LatLng point) {
    mDismissOverlay.show();
}

@Override
public void onMapReady(GoogleMap map) {
    mMap = map;
    mMap.addMarker(new MarkerOptions().position(SYDNEY)
        .title("Sydney Opera House"));
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 10));
    mMap.setOnMapLongClickListener(this);
}

App Invite

Intent intent = new AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title))
        .setMessage(getString(R.string.invitation_message))
        .setDeepLink(Uri.parse(getString(R.string.invitation_deep_link)))
        .build();
startActivityForResult(intent, REQUEST_INVITE);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);

    if (requestCode == REQUEST_INVITE) {
        if (resultCode == RESULT_OK) {
            // Check how many invitations were sent and log a message
            // The ids array contains the unique invitation ids for each invitation sent
            // (one for each contact select by the user). You can use these for analytics
            // as the ID will be consistent on the sending and receiving devices.
            String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data);
            Log.d(TAG, getString(R.string.sent_invitations_fmt, ids.length));
        } else {
            // Sending failed or it was canceled, show failure message to the user
            showMessage(getString(R.string.send_failed));
        }
    }
}
    

Already Installed

<receiver
    android:name="com.google.android.gms.samples.appinvite.ReferrerReceiver"
    android:exported="true"
    tools:ignore="ExportedReceiver">
    <intent-filter>
        <action android:name="com.android.vending.INSTALL_REFERRER" />
    </intent-filter>
</receiver>

Not Installed (1/2)

public class ReferrerReceiver extends BroadcastReceiver {

    public ReferrerReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) {
        // Create deep link intent with correct action and add play store referral information
        Intent deepLinkIntent = AppInviteReferral.addPlayStoreReferrerToIntent(intent,
                new Intent(context.getString(R.string.action_deep_link)));

        // Let any listeners know about the change
        LocalBroadcastManager.getInstance(context).sendBroadcast(deepLinkIntent);
    }
}

Not Installed (2/2)

private void registerDeepLinkReceiver() {
    // Create local Broadcast receiver that starts DeepLinkActivity when a deep link
    // is found
    mDeepLinkReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (AppInviteReferral.hasReferral(intent)) {
                launchDeepLinkActivity(intent);
            }
        }
    };

    IntentFilter intentFilter = new IntentFilter(getString(R.string.action_deep_link));
    LocalBroadcastManager.getInstance(this).registerReceiver(
            mDeepLinkReceiver, intentFilter);
}

private void unregisterDeepLinkReceiver() {
    if (mDeepLinkReceiver != null) {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mDeepLinkReceiver);
    }
}

/**
 * Launch DeepLinkActivity with an intent containing App Invite information
 */
private void launchDeepLinkActivity(Intent intent) {
    Log.d(TAG, "launchDeepLinkActivity:" + intent);
    Intent newIntent = new Intent(intent).setClass(this, DeepLinkActivity.class);
    startActivity(newIntent);
}

Google FIt

  • RecordingApi
  • WorkoutExercises

Q & A

Made with Slides.com