Testing account kit

Making a test plan for account kit
common flows:
1. User logs in with phone number
2. User logs in with email

How can the Graph API make app better?
-increased personalization
-more opportunities for social interaction

Better personalization and more opportunities for social interaction can lead to improvements in:
-Engagement
-Retention

Facebook Graph API
https://developers.facebook.com/docs/graph-api?locale=ja_JP

if (accessToken != null &&
accessToken.getPermissions().contains("user_friends")){
	// make the API call to fetch friends list
	Bundle parameters = new Bundle();
	parameters.putString("fields", "picture");
	new GraphRequest(
	AccessToken.getCurrentAccessToken(), "/me/friends",HttpMethod.GET,...);
}.executeAsync();

Logout

Implementing the logout button manually

public void onLogout(View view){
	AccountKit.logOut();
	LoginManager.getInstance().logOut();
	launchLoginActivity();
}

How to Test: Making a Test Plan
unexpected conditions:
1.A new user declines to authenticate permissions once, then tries to log in again
2.A returning user who has changed their password
3.A returning user with an expired token
4.A returning user who logs in after disabling the Facebook platform

Account kit
Common Flows:
1. A user logs in with a phone number
2. A user logs in with an email address

Unexpected conditions:
1. A user tries to log in, but does not receive the SMS
2. A user types in the wrong code

Custom Tab Activity

<activity
	android:name="com.facebook.CustomTabActivity"
	android:exported="true">
	<intent-filter>
		<action android:name="android.intent.action.VIEW" />
		<category android:name="android.intent.category.DEFAULT" />
		<category android:name="android.intent.category.BROWSABLE" />
		<data android:scheme="@string/fb_login_protocol_scheme" />
	</intent-filter>
</activity>
@Override
protected void onActivityResult(final int requestCode,
final int resultCode, final Intent data){
	super.onActivityResult(requestCode, resultCode, data);

	callbackManager.onActivityResult(requestCode, resultCode, data);
	...
}
if (AccessToken.getCurrentAccessToken() != null){
	...
}
else{
	AccountKit.getCurrentAccount(new AccountKitCallback<Account>(){
		...
	})
}

if (AccessToken.getCurrentAccessToken() != null){
	Profile profile = Profile.getCurrentProfile();
}

if (AccessToken.getCurrentAccessToken() != null){
	Profile currentProfile = Profile.getCurrentProfile();
	if (currentProfile != null){
		displayProfileInfo(currentProfile);
	} else {
		Profile.fetchProfileForCurrentAccessToken();
	}
}

AuthCode

AccountKitConfiguration.AccountKitConfigurationBuilder configurationBuilder = new AccountKitConfiguration.AccountKitConfigurationBuilder(
	loginType,
	AccountKitActivity.ResponseType.CODE
);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
	super.onActivityResult(requestCode, resultCode, data);
		AccountKitLoginResult loginResult = data.getParcelableExtra(
			AccountKitLoginResult.RESULT_KEY);
		String toastMessage;
		if(loginResult.getError() != null){
			toastMessage = loginResult.getError().getErrorType().getMessage();
			showErrorActivity(loginResult.getError());
		else if (loginResult.wasCancelled());
			toastMessage = "Login Canceled";
		} else {
			String authCode = loginResult.getAuthorizationCode();
			startActivity(new Intent(this, AuthorizedActivity.class));
		}

}
{
	"id" : <account_kit_user_id>,
		"access_token" : <account_access_token>,
		"token_refresh_interval_sec" : <refresh_interval>
}

GET https://graph.accountkint.com/v1.v1/me/?access_token=

{
	"id":"12345"
	"phone":{
		"number":"+15551234567"
		"country_prefix":"1",
			"national_number":"5551234567"
	}
}
public void getAccountInfo(AccountInfo accountInfo){
	String accountKitId = accountInfo.getId();
	String phoneNumber = accountInfo.getPhone().getNumber();

	String email = accountInfo.getEmail();
}
AppEventsLogger logger = AppEventsLogger.newLogger(this)

public void onLoginSMS(final View view){
	logger.logEvent("onSMSLogin");
	onLogin(LoginType.PHONE);
}

public void onLoginEmail(final View view){
	logger.logEvent("onEmailLogin");
	onLogin(LoginType.EMAIL);
}

AndroidManifest.xml

<application
	android:allowBackup="true"
	android:icon="@mipmap/ic_launcher"
	android:label="@string/app_name"
	android:roundIcon="@mipmap/ic_launcher_round"
	android:supportsRtl="true"
	android:theme="@style/AppTheme">
	<meta-data android:name="com.facebook.accountkit.ApplicationName"
		android:value="@string/app_name" />
	<meta-data android:name="com.facebook.sdk.ApplicationId"
		android:value="@string/FACEBOOK_APP_ID" />
	<meta-data android:name="com.facebook.accountkit.ClientToken"
		android:value="@string/ACCOUNT_KIT_CLIENT_TOKEN" />

	<activity
		android:name="com.facebook.accountkit.ui.AccountKitActivity"
		android:theme="@style/AppLoginTheme"
		tools:replace="android:theme">
	</activity>
<resources>
	<style name="AppLoginTheme" parent="Theme.AccountKit"/>
</resources>
import com.facebook.accountkit.AccountKit;
import com.facebook.accountkit.AccessToken;

@Override
Protected void onCreate(Bundle savedInstanceState){
	AccessToken accessToken = AccountKit.getCurrentAccessToken();
	if (accessToken != null){
		launchAccountActivity();
	}
}

Launch Activity View

import UIKit

class ViewController: UIViewController {

	override func viewDidLoad(){
		super.viewDidLoad()
	}

	@IBAction func experiment(){
		let image = UIImage()
		let controller = UIActivityViewController(activityItems: [image],
			applicationActivities: nil)
		self.presentViewController(controller, animated: true, completion:nil)
	}
}
@IBAction func experiment(_ sender: Any){
	let nextController = UIImagePickerController()
	self.present(nextController, animated: true, completion: nil)
}

@IBAction func experiment(_ sender: Any){

	let image = UIImage()
	let controller = UIActivityViewController(activityItems: [image],
		self.present(controller, animated: true, completation: nil))
}
import UIKit

class DiceViewController: UIViewController {

	var firstValue: Int?
	var secondValue: Int?

	@IBOutlet var firstDie: UIImageView!
	@IBOutlet var secondDie: UIImageView!

	override func viewWillAppear(animated: Bool){

		if let firstValue = self.firstValue {
			self.firstDie.image = UIImage(named: "d\(firstValue)")
		} else {
			self.firstDie.image = nil;
		}

		if let secondValue = self.secondValue {
			self.secondDie.image = UIImage(named: "d\(secondValue)")
		} else {
			self.secondDie.image = nil
		}

		self.firstDie.alpha = 0
		self.secondDie.alpha = 0
	}
}

Implementation Steps

Name, Service, Conditions, Event Handler
Name:makeUppercase, Realtime Database, Trigger on specific path, makeUppercase handler

const functions = require('firebase-functions');

exports.emojify = functions.database.ref('/messages/{pushId}/text')
	.onWrite(event => {
		if(!event.data.val()|| event.data.previous.val()){
			console.log("not a new write event");
			return;
		}

		console.log("emojifying");

		const originalText = event.data.val();
		const emojifiedText = emojifyText(originalText);

		return event.data.ref.set(emojifiedText);
	});

	function emojifyText(text){
		var emojifiedText = text;
		emojifiedText = emojifiedText.replace(/\blob\b/id, "");
		emojifiedText = emojifiedText.replace(/\blob\b/id, "");
		return emojifiedText;
	}

FirebaseUI Authentication

Adding Sign In
-Email/Password
-Google
-Facebook
-Twitter
-GitHub

mPhotoPickerButton.setOnClickListener(new View.OnClickListener(){
	@Override
	public void onClick(View view){
		Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
		intent.setType("image/jpeg");
		intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
		startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER);
	}
});
service firebase.storage {
	match /b/friendlychat-12987.appspot.com/o {
		match /{allPaths=**} {
			allow read, write: if request.auth != null;
		}
	}
}

match /funGifs/cats/{catPics=**}

resource: existing storage data
request: incoming request data

allow read: if request.auth != null && imageId.matches(“.*.gif”)

String name = "Firebase":
name = 3;

var name = "Firebase";
name = 3;

final x = 42;
x = 1024;

const x = 42;
x = 1024

var x = 42;
var y = 1024;

System.out.println("Message");
Log.d(TAG, "Message");

public int multiply(int a, int b){
	return a * b;
}

String[] list = {"apple", "pear", "orange"};

for (int i = 0; i < list.length; i++){
	System.out.println(list[i])
}

Databse implementation

mSendButton.setOnClickListener(new View.OnClickListener(){
	@Override
	public void onClick(View view){
		mMessageEditText.setText("");
	}
});
{
	"rules": {
		".read": true,
		".write": ture
	}
}

Database Security
-Database security rules
-Security rule configuration examples
-Authentication with Firebase

{
	"chat": {
		"messages": {
			"-KS3PV-iwUZp5wkNq70s":{
				"name": "person1",
				"text": "hey!"
			},
			"-KS3PXhIhs8J_inrExy4":{
				"name": "person2",
				"text": "what's up?"
			}
		}
	},
	"special_chat": {
		"messages": {
			"-KR-DwqtKzlWGxSn9P0y":{
				"name": "person1",
				"text":"want to go to the movies?"
			},
			"-KR4tIpWmNn-EYxquSrw": {
				"name": "person3",
				"text": "Yeah! let's meet at 7."
			}
		}
	},
	"users": {
		"uid1":{
			"paid":true
		},
		"uid2": {
			"paid": false
		},
		"uid3": {
			"paid": true
		}
	}

}

Firebase Realtime Database

what is firebase databse?
what can it be used for?
realtime database in action
database structure best practices

“:
“golden”: 1.618
“fog”: true
“palindrome”: “tacocat”
“messages”: “message1”: “name”:”person”, “text”:”Hello?”
“message2”: “name”:”Individual”, “text”:”Hi!”

node should be “child” and “parent

{
	"questions": {
		"ABCDakarandomkey": {
			"question": "Who was the 13th president of the United States?"
			"choice_1": "Millard Fillmore",
			"choice_2": "Zachary Taylor",
			"choice_3": "Franklin Pierce",
			"choice_4": "James K. Polk",
			"answer" :"choice_1"
		},
		"EFGHakarandomkey":{
			"question": "In what year was the first gasoline combustion engine invented?",
			"choice_1":"1769",
			"choice_2":"1886",
			"choice_3":"1807",
			"choice_4":"1864",
			"answer": "choice_4"
		}
	},
	"players":{
		"user_key_1":{
			"name":"Person",
			"opponents":{
				"IJKLakarandomkey":"user_key_2",
				"MNOPakarandomkey":"user_key_6"
			},
			"questions":{
				"ABCDakarandomkey": "Correct",
				"EFGHakarandomkey": "Incorrect"
			}
		},
		"user_key_2": {
			"name": "Mai",
			"opponents": {
				"QRAAakarandomkey": "user_key_1",
				"SQUEakarandomkey": "user_key_6"
			},
			"questions": {
				"ABCDakarandomkey":"Incorrect",
				"EFGHakarandomkey":"Incorrect"
			}
		}
	},
	"opponents": {
		"couple_Key_1": "user_key_1_user_key_2",
		"user_1": "user_key_1",
		"user_2": "user_key_2",
		"winner": "user_key_1"
	}
}