導入部分の話が面白い
- ムーアの法則
- 昔の PC の話とか
- 昔のネット通信のモデムの音とか
- アーサー・C・クラークの言葉
"Any significantly advanced technology is indistinguishable from magic." "高度に発達したテクノロジーは魔法と見分けがつかない" - 5年前まだ Android がないときにブログに書いた未来の予想が悲観的すぎた、予想よりも早く実現しつつある
- アーサー・C・クラークの言葉その2
"The only way of discovering the limits of the possible is to venture a little way past it into the impossible."
Android Beam の話
# NFC の機能が魔法っぽいってはなしかな
Lockscreen Widget
# Lockscreen Widget って魔法っぽいかな。。。?
"Context isn't important, it's critical."
デバイスはユーザーのことを知っている。デバイスはどんなニュースや本を読んで、どんな音楽を聴いて、どんな映画を見て、どんなゲームをして、だれが友達で、どこで予定があるのか知っている
魔法のような体験を作るための Context を用意する、すごい可能性がここにはある
どんな Context が使える?
例えば Location
Location based Services
Google Play Services の一部として新しい Location based Services をリリースしたよ!
すごい簡単に使えるよ
単にクライアントを作って接続すれば OK
- private void connectLBS() {
- int gpsExists = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
- if(gpsExists == ConnectionResult.SUCCESS) {
- mLocationClient = new LocationClient(this, this, this);
- mLocationClient.connect();
- }
- }
- @Override
- public void onConnected(Bundle connectionHint) {
- requestUpdates(mLocationClient);
- }
Google Play Services の一部である Fused Location Provider が最もいい結果を返してくれる
Geofencing
- List<Geofence> fenceList = new ArrayList<Geofence>();
- // TODO Repeat for all Geofences
- Geofence geofence = new Geofence.Builder()
- .setRequestId(mKey)
- .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
- Geofence.GEOFENCE_TRANSITION_EXIT)
- .setCircularRegion(latitude, longitude, GEOFENCE_RADIUS)
- .setExpirationDuration(Geofence.NEVER_EXPIRE)
- .build();
- fenceList.add(geofence);
- mLocationClient.addGeofences(fenceList, pendingIntent, addGeofenceResultListener);
Activity Recognition
- Intent intent = new Intent(this, ActivityRecognitionIntentService.class);
- intent.setAction(MyActivity.ACTION_STRING);
- PendingIntent pi =
- PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
- mActivityRecognitionClient.requestActivityUpdates(interval, pi);
- @Override
- protected void onHandleIntent(Intent intent) {
- if(intent.getAction() == MyActivity.ACTION_STRING) {
- if(ActivityRecognitionResult.hasResult(intent)) {
- ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
- DetectedActivity detectedActivity = result.getMostProbableActivity();
- int activityType = detectedActivity.getType();
- if (activityType == DetectedActivity.STILL)
- setUpdateSpeed(PAUSED);
- else if (activityType == DetectedActivity.IN_VEHICLE)
- setUpdateSpeed(FASTER);
- else
- setUpdateSpeed(REGULAR);
- }
- }
- }
Google+ について
友達の状態とかがとれるのでもっと Context にあったことができるという話
(どうなんでしょうね。。。)
知りすぎると Uncanny App Valley に落ちるよ
Google Now は何をするのかはっきりわかるけど、もしソフトキーボードが自分の好きなスポーツを勧めてきたりしたら気持ち悪いよねってこと
Rob Foster の言葉
"Introducing visceral elements into an app.. will make it speak to the subconscious."
人生は単になにをするかだけではなく、何を体験するかだから
カフェで人は単にコップのなかの水を買っているわけではなく。その周りにひろがる体験(雰囲気とか香りとか見た目とかもろもろ)を買っている
アプリでは何ができる?
・テイストはだめだよね
・香り、、、もだめだよね、来年どうなるかみよう
ということで
・見た目
・音
・タッチ
にフォーカスしよう
背後にある哲学、スピリット、ビジョンを理解しよう
→ https://developer.android.com/design をみよう
Text to Speech(TTS)
セットアップは簡単
- private void initTextToSpeech() {
- Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
- startActivityForResult(intent, TTS_DATA_CHECK);
- }
- @Override
- protected void onActivityResult(int request, int result, Intent data) {
- if(request == TTS_DATA_CHECK && result == Engine.CHECK_VOICE_DATA_PASS) {
- tts = new TextToSpeech(this, new OnInitListener() {
- @Override
- public void onINit(int status) {
- if (status == TextToSpeech.CUSSCESS)
- ttsIsInit = true;
- }
- });
- }
- else {
- startActivity(new Intent(Engine.ACTION_INSTALL_TTS_DATA);
- }
- }
- private void say(String text) {
- if (tts != null && ttsIsInit) {
- tts.speak(text, TextToSpeech.QUEUE_ADD, null);
- }
- }
Speech Recognition
- private void requestVoiceInput() {
- Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
- RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
- intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
- getString(R.string.voice_input_prompt);
- intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,
- Locale.ENGLISH);
- startActivityForResult(intent, VOICE_RECOGNITION);
- }
- @Override
- protected void onActivityResult(int request, int result, Intent data) {
- if (request == VOICE_RECOGNITION && result == RESULT_OK) {
- ArrayList<string> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
- String mostLikelyResult = results[0];
- useSpeechInput(mostLikelyResult);
- }
- }
- </string>
48dip のレイアウト
どれがタッチできるのかユーザーがわかるようにする = ちゃんと state に応じた画像を用意しよう
android:foreground="?android:selectableItemBackground"
Simple Accessibility Support
- <Button
- ...
- android:contentDescription="@string/my_button_description"
- />
Custom Control Accessibility Support
- public void setHeading(float heading) {
- mHeading = heading;
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
- }
- @Override
- public boolean dispatchPopulateAccessibilityEvent(final AccessibilityEvent e) {
- super.dispatchPopulateAccessibilityEvent(e);
- String heading = String.valueOf(mHeading);
- if(heading.length() > AccessibilityEvent.MAX_TEXT_LENGTH) {
- heading = heading.subString(0, AccessibilityEvent.MAX_TEXT_LENGTH);
- }
- event.getText().add("Heading is " + heading + " degree");
- return true;
- }
ジェスチャーを使う Android Training の Using Touch Gestures クラスがおすすめ
Jazz Hands
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getAction();
- if (event.getPointerCount() > 1) {
- int actionPointerId = action & MotionEvent.ACTION_POINTER_ID_MASK;
- int actionEvent = action & MotionEvent.ACTION_MASK;
- int pointerIndex = event.findPointerIndex(actionPointerId);
- int xPos = (int) event.getX(pointerIndex);
- int yPos = (int) event.getY(pointerIndex);
- // TODO Magic.
- }
- }
ルールを破るときは気をつけて、かならずしも positive な反応になるとは限らない
→ Google Play の developer console に追加された ALPHA TESTING, BETA TESTING 機能を使おう
だれがもっともバリューゾーンのユーザーなのかを知ろう、国、言語、デバイス、、、
→ Analytics を使おう
# FFっぽい動画でてきたでw


# Google Quest Cheat Codes...w

スマートフォンの一番の魔法は常にネットに繋がっていることじゃないかな
更新ボタンを eliminate するのはやめよう
ユーザーが更新しようとする前に更新しといてほしいよね
データのやりとりの理由としては
- Client Updates
- Server Updates
- On-Demand Downloads
- Cross-Device Updates
Cross-Device updates にも GCM が使えるよ
キーノートのデモであったように Notification がデバイス間で同期するようになったよ
Google Cloud Messaging: Upstream
- GoogleCloudMessaging gcm = GoogleCloudMessaging.get(context);
- gcm.send(to, msgId, data);
Client Updates にとてもいいものがあるよ、それが SyncAdapter
- public class MySyncAdapter extends AbstractThreadSyncAdapter {
- public MySyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
- super(context, autoInitialize, allowParallelSyncs);
- }
- public MySyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
- @Override
- public void onPerformSync(Account account, Bundle extras, String authority,
- ContentProviderCLient provider, SyncResult syncResult) {
- // TODO Synchronize your data between client adn server.
- }
- }
Abstract Account Manager
- public class MyAccountAuthenticator extends AbstractAccountAuthenticator {
- public static final String ACCOUNT_TYPE = "com.mycompany.myapp";
- public static final String ACCOUNT_NAME = "MY STUB ACCOUNT";
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
- String authTokenType, String[] requiredFeatures, Bndle options)
- throw NetworkErrorException {
- AccountManager manager = AccountManager.get(activity);
- final Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
- manager.addAccountExplicitly(account, null, null);
- ContentResolver.setIsSyncable(account, authority, 1);
- ContentResolver.setSyncAutomatically(account, authority, true);
- return null;
- }
- ...
- }
Account Manager Service
- public class MyAuthenticationService extends Service {
- MyAccountAuthenticator mAuthenticator;
- @Override
- public void onCreate() {
- mAuthenticator = new MyAccountAuthenticator(this);
- }
- @Override
- public IBinder onBind(Intent intent) {
- return mAuthenticator.getIBinder();
- }
- }
- <account-authenticator xmlns:android="..."
- android:accountType="com.mycompany.myapp"
- android:icon="@drawable/icon"
- android:smallIcon="@drawable/miniicon"
- android:label="@string/app_name"
- />
- public class MyContentProvider extends ContentProvider {
- @Override
- public boolean onCreate() { return true; }
- @Override
- public String getType(Uri uri) { return "vnd.android.cursor.dir/vnd.myapp.items"; }
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sort) { return null; }
- @Override
- public Uri insert(Uri uri, ContentValues initialValues) { return null; }
- @Override
- public int delete(Uri uri, String where, String[] whereArgs) { return 0; }
- @Override
- public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { return 0; }
- }
- <provider android:authorities="com.mycompany.myapp.myauthority"
- android:name=".content_providers.PlaceDetailsContentProvider" />
- <sync-adapter xmlns:android="..."
- android:contentAuthority="com.mycompany.myapp.myauthority"
- android:accountType="com.mycompany.myapp"
- android:userVisible="false"
- />
- <provider android:authorities="com.mycompany.myapp.myauthority"
- android:name=".content_providers.PlaceDetailsContentProvider" />
- <service android:name=".MyAuthenticationService" android:exported="true">
- <intent-filter>
- <action android:name="android.accounts.AccountAuthenticator" />
- </intent-filter>
- <meta-data
- android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/accountauth" />
- </service>
- <service android:name=".MySyncService" android:exported="true">
- <intent-filter>
- <action android:name="android.content.SyncAdapter" />
- </intent-filter>
- <meta-data
- android:name="android.content.SyncAdapter"
- android:resource="@xml/sync_myapp" />
- </service>
- final Account account = new Account(null, MyAccountAuthenticator.ACCOUNT_TYPE);
- String authority = "com.mycompany.myapp.myauthority";
- mContentResolver.requestSync(account, authority, null);
- final Account account = new Account(null, MyAccountAuthenticator.ACCOUNT_TYPE);
- String authority = "com.mycompany.myapp.myauthority";
- long interval = 12 * 60 * 60; // 12 hours (24hr by default).
- mContentResolver.addPeriodicSync(account, authority, null, interval);
時間をベースにして同期はやめよう
たとえネットに接続しなくても次のようにランダムな jitter を使おう
Update Window
- final Account account = new Account(null, MyAccountAuthenticator.ACCOUNT_TYPE);
- String authority = "com.mycompany.myapp.myauthority";
- Random random = new Random();
- int jitter = random.nextInt(60);
- long start = SystemClock.elapseRealtime() + interval - ((30 + jitter)*60000);
- alarmManager.setInexactRepeating(alamType, start, interval, pi);
- @Override
- public void onReceive(Context context, Intent intent) {
- // Extract the Activity that's been detected.
- ActivityDetectionResult activity = ActivityDetectionResult.extractResult(intent);
- if(activity != null) {
- ActivityType activityType = activity.getMostProvavleActivity().getType();
- if (ActivityType.valueOf(activityTypeString) == ActivityType.TILTING)
- context.startService(new Intent(context, DailyUpdateService.class);
- }
- }
On-demand download
SyncAdapter with Calls to Other Updates
- @Override
- public void onPerformSync(Account account, Bundle extras, String authority,
- ContentProviderClient provider, SyncResult result) {
- downloadServerSideSync();
- uploadClientSideSync();
- transmitBatchedAnalytics();
- executePrefetch();
- retryFiledTransfers();
- }
# 1時間という長いセッションだったし、最後のほうはコードが多かったな # 3番目の動画がいいね
0 件のコメント:
コメントを投稿