├── .github └── dependabot.yml ├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── schemas │ └── com.nfjs.helloworldas.AppDatabase │ │ └── 1.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── nfjs │ │ └── helloworldas │ │ ├── DatabaseAdapterTest.java │ │ └── UserRoomTest.java │ ├── arrogant │ └── res │ │ ├── drawable-mdpi │ │ └── animal.png │ │ └── values │ │ └── strings.xml │ ├── friendly │ └── res │ │ ├── drawable-mdpi │ │ └── animal.jpg │ │ └── values │ │ └── strings.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── nfjs │ │ │ └── helloworldas │ │ │ ├── AppDatabase.java │ │ │ ├── DatabaseAdapter.java │ │ │ ├── DatabaseHelper.java │ │ │ ├── MyActivity.java │ │ │ ├── NameFragment.java │ │ │ ├── User.java │ │ │ ├── UserDAO.java │ │ │ └── WelcomeActivity.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── layout-land │ │ └── activity_my.xml │ │ ├── layout │ │ ├── activity_my.xml │ │ └── activity_welcome.xml │ │ ├── menu │ │ ├── my.xml │ │ └── welcome.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── obsequious │ └── res │ ├── drawable-mdpi │ └── animal.jpeg │ └── values │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gradle 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: com.android.tools.build:gradle 11 | versions: 12 | - 4.1.2 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | /.idea/libraries 5 | .DS_Store 6 | /build 7 | .idea/ 8 | *.iml 9 | 10 | # Generated files 11 | bin/ 12 | gen/ 13 | 14 | # Gradle files 15 | build/ 16 | 17 | # Proguard folder generated by Eclipse 18 | proguard/ 19 | 20 | # Log Files 21 | *.log 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HelloWorldAS 2 | ============ 3 | 4 | Android "Hello, World" example, using Android Studio 5 | 6 | This simple application is used during my Android talks at No Fluff, Just Stuff (http://nofluffjuststuff.com) conferences. 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | .idea/ 3 | .iml -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | 6 | defaultConfig { 7 | applicationId "com.nfjs.helloworldas" 8 | minSdkVersion 16 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | javaCompileOptions { 14 | annotationProcessorOptions { 15 | arguments = ["room.schemaLocation": 16 | "$projectDir/schemas".toString()] 17 | } 18 | } 19 | 20 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | flavorDimensions('attitude') 31 | 32 | productFlavors { 33 | arrogant { 34 | dimension 'attitude' 35 | } 36 | 37 | friendly { 38 | dimension 'attitude' 39 | } 40 | 41 | obsequious { 42 | dimension 'attitude' 43 | } 44 | } 45 | 46 | compileOptions { 47 | sourceCompatibility JavaVersion.VERSION_1_8 48 | targetCompatibility JavaVersion.VERSION_1_8 49 | } 50 | 51 | sourceSets { 52 | androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) 53 | } 54 | } 55 | 56 | dependencies { 57 | implementation fileTree(dir: 'libs', include: ['*.jar']) 58 | annotationProcessor 'androidx.room:room-compiler:2.5.1' 59 | implementation 'androidx.room:room-runtime:2.5.1' 60 | testImplementation 'androidx.room:room-testing:2.5.1' 61 | androidTestImplementation 'junit:junit:4.13.2' 62 | androidTestImplementation 'androidx.test:core:1.3.0' 63 | androidTestImplementation 'androidx.test:runner:1.5.2' 64 | androidTestImplementation 'androidx.test:rules:1.3.0' 65 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 66 | } 67 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/23.0.2/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/schemas/com.nfjs.helloworldas.AppDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "6110314c4f2c198b05e107325e962007", 6 | "entities": [ 7 | { 8 | "tableName": "users", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`uid`))", 10 | "fields": [ 11 | { 12 | "fieldPath": "uid", 13 | "columnName": "uid", 14 | "affinity": "INTEGER", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "name", 19 | "columnName": "name", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | } 23 | ], 24 | "primaryKey": { 25 | "columnNames": [ 26 | "uid" 27 | ], 28 | "autoGenerate": false 29 | }, 30 | "indices": [], 31 | "foreignKeys": [] 32 | } 33 | ], 34 | "setupQueries": [ 35 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 36 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6110314c4f2c198b05e107325e962007\")" 37 | ] 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/nfjs/helloworldas/DatabaseAdapterTest.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import androidx.test.core.app.ApplicationProvider; 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import java.util.List; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertFalse; 14 | import static org.junit.Assert.assertTrue; 15 | import static org.junit.Assert.fail; 16 | 17 | @RunWith(AndroidJUnit4.class) 18 | public class DatabaseAdapterTest { 19 | private DatabaseAdapter dba; 20 | 21 | @Before 22 | public void setUp() throws Exception { 23 | dba = new DatabaseAdapter(ApplicationProvider.getApplicationContext()); 24 | dba.open(); 25 | dba.deleteAllNames(); 26 | } 27 | 28 | @Test 29 | public void testInsertAndGetAllNames() { 30 | dba.insertName("Fred"); 31 | dba.insertName("Barney"); 32 | List names = dba.getAllNames(); 33 | assertTrue(names.contains("Fred")); 34 | assertTrue(names.contains("Barney")); 35 | } 36 | 37 | @Test 38 | public void testExceptionForEmptyName() { 39 | try { 40 | dba.insertName(""); 41 | fail("Should never get here"); 42 | } catch(IllegalArgumentException e) { 43 | assertEquals("Name must not be empty", e.getMessage()); 44 | } 45 | } 46 | 47 | @Test 48 | public void testDeleteName() { 49 | int count = dba.getAllNames().size(); 50 | dba.insertName("Fred"); 51 | assertEquals(count + 1, dba.getAllNames().size()); 52 | 53 | dba.deleteName("Fred"); 54 | assertEquals(count, dba.getAllNames().size()); 55 | } 56 | 57 | @Test 58 | public void testExists() { 59 | assertFalse(dba.getAllNames().contains("Fred")); 60 | 61 | dba.insertName("Fred"); 62 | assertTrue(dba.getAllNames().contains("Fred")); 63 | 64 | dba.deleteName("Fred"); 65 | assertFalse(dba.getAllNames().contains("Fred")); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/nfjs/helloworldas/UserRoomTest.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import androidx.room.Room; 4 | import androidx.test.core.app.ApplicationProvider; 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import android.content.Context; 8 | 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.util.List; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | @RunWith(AndroidJUnit4.class) 21 | public class UserRoomTest { 22 | private UserDAO dao; 23 | private AppDatabase db; 24 | 25 | @Before 26 | public void createDb() { 27 | Context context = ApplicationProvider.getApplicationContext(); 28 | db = Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build(); 29 | dao = db.getUserDao(); 30 | } 31 | 32 | @After 33 | public void closeDb() { 34 | db.close(); 35 | } 36 | 37 | @Test 38 | public void testInsertAndGetAllNames() { 39 | User fred = new User("Fred"); 40 | User barney = new User("Barney"); 41 | dao.insertUsers(fred, barney); 42 | List users = dao.getAllUsers(); 43 | assertTrue(users.contains(fred)); 44 | assertTrue(users.contains(barney)); 45 | } 46 | 47 | @Test 48 | public void testDeleteUser() { 49 | User fred = new User("Fred"); 50 | 51 | int count = dao.getAllUsers().size(); 52 | dao.insertUsers(fred); 53 | assertEquals(count + 1, dao.getAllUsers().size()); 54 | 55 | dao.deleteUsers(fred); 56 | assertEquals(count, dao.getAllUsers().size()); 57 | } 58 | 59 | @Test 60 | public void testExists() { 61 | User fred = new User("Fred"); 62 | 63 | assertFalse(dao.getAllUsers().contains(fred)); 64 | 65 | dao.insertUsers(fred); 66 | assertTrue(dao.getAllUsers().contains(fred)); 67 | 68 | dao.deleteUsers(fred); 69 | assertFalse(dao.getAllUsers().contains(fred)); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/arrogant/res/drawable-mdpi/animal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/arrogant/res/drawable-mdpi/animal.png -------------------------------------------------------------------------------- /app/src/arrogant/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | His/Her Royal Highness 4 | Arrogant 5 | We condescend to acknoweldge your 6 | presence, if just barely, %1$s. 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/friendly/res/drawable-mdpi/animal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/friendly/res/drawable-mdpi/animal.jpg -------------------------------------------------------------------------------- /app/src/friendly/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | We are BFFs! 4 | Friendly 5 | Hi there, %1$s! 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import androidx.room.Database; 4 | import androidx.room.RoomDatabase; 5 | 6 | @Database(entities = { User.class }, version = 1) 7 | public abstract class AppDatabase extends RoomDatabase { 8 | public abstract UserDAO getUserDao(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/DatabaseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class DatabaseAdapter { 12 | private DatabaseHelper dbHelper; 13 | private SQLiteDatabase database; 14 | 15 | public DatabaseAdapter(Context context) { 16 | dbHelper = new DatabaseHelper(context.getApplicationContext()); 17 | } 18 | 19 | public DatabaseAdapter open() { 20 | database = dbHelper.getWritableDatabase(); 21 | return this; 22 | } 23 | 24 | public void close() { 25 | dbHelper.close(); 26 | } 27 | 28 | private Cursor getAllEntries() { 29 | String[] columns = new String[1]; 30 | columns[0] = "name"; 31 | return database.query("names", columns, null, null, null, null, null); 32 | } 33 | 34 | public List getAllNames() { 35 | ArrayList names = new ArrayList<>(); 36 | Cursor cursor = getAllEntries(); 37 | if (cursor.moveToFirst()) { 38 | do { 39 | names.add(cursor.getString(0)); 40 | } while (cursor.moveToNext()); 41 | } 42 | cursor.close(); 43 | return names; 44 | } 45 | 46 | public boolean exists(String name) { 47 | Cursor cursor = database.rawQuery( 48 | "select name from names where name=?", 49 | new String[]{ name }); 50 | boolean response = cursor.getCount() >= 1; 51 | cursor.close(); 52 | return response; 53 | } 54 | 55 | public long insertName(String name) { 56 | if (name.length() == 0) { 57 | throw new IllegalArgumentException("Name must not be empty"); 58 | } 59 | ContentValues values = new ContentValues(); 60 | values.put("name", name); 61 | return database.insert("names", null, values); 62 | } 63 | 64 | public int deleteName(String name) { 65 | String whereClause = "name = ?"; 66 | String[] whereArgs = new String[1]; 67 | whereArgs[0] = name; 68 | return database.delete("names", whereClause, whereArgs); 69 | } 70 | 71 | public int deleteAllNames() { 72 | return database.delete("names", null, null); 73 | } 74 | 75 | public int updateName(String name) { 76 | String whereClause = "name = ?"; 77 | String[] whereArgs = new String[1]; 78 | whereArgs[0] = name; 79 | ContentValues values = new ContentValues(); 80 | values.put("name", name); 81 | return database.update("names", values, whereClause, whereArgs); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | public class DatabaseHelper extends SQLiteOpenHelper { 8 | private static final String DATABASE_NAME = "names.db"; 9 | private static final int VERSION = 1; 10 | 11 | public DatabaseHelper(Context context) { 12 | super(context, DATABASE_NAME, null, VERSION); 13 | } 14 | 15 | @Override 16 | public void onCreate(SQLiteDatabase db) { 17 | db.execSQL("CREATE TABLE NAMES( _id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);"); 18 | } 19 | 20 | @Override 21 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 22 | db.execSQL("DROP TABLE IF EXISTS NAMES;"); 23 | onCreate(db); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/MyActivity.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.EditText; 12 | import android.widget.TextView; 13 | 14 | 15 | public class MyActivity extends Activity { 16 | private static final String TAG = "MyActivity"; 17 | 18 | private TextView textView; 19 | private EditText editText; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_my); 25 | 26 | Log.d(TAG, "onCreate"); 27 | textView = (TextView) findViewById(R.id.text_view); 28 | editText = (EditText) findViewById(R.id.edit_text); 29 | Button helloButton = (Button) findViewById(R.id.hello_button); 30 | helloButton.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View v) { 33 | sayHello(v); 34 | } 35 | }); 36 | } 37 | 38 | public void sayHello(View view) { 39 | String name = editText.getText().toString(); 40 | textView.setText(String.format("Hello, %1$s!", name)); 41 | 42 | Intent intent = new Intent(this, WelcomeActivity.class); 43 | intent.putExtra("name", name); 44 | startActivity(intent); 45 | } 46 | 47 | @Override 48 | protected void onStart() { 49 | super.onStart(); 50 | Log.d(TAG, "onStart"); 51 | } 52 | 53 | @Override 54 | protected void onResume() { 55 | super.onResume(); 56 | Log.d(TAG, "onResume"); 57 | } 58 | 59 | @Override 60 | protected void onPause() { 61 | super.onPause(); 62 | Log.d(TAG, "onPause"); 63 | } 64 | 65 | @Override 66 | protected void onStop() { 67 | super.onStop(); 68 | Log.d(TAG, "onStop"); 69 | } 70 | 71 | @Override 72 | protected void onDestroy() { 73 | super.onDestroy(); 74 | Log.d(TAG, "onDestroy"); 75 | } 76 | 77 | @Override 78 | protected void onRestart() { 79 | super.onRestart(); 80 | Log.d(TAG, "onRestart"); 81 | } 82 | 83 | @Override 84 | public boolean onCreateOptionsMenu(Menu menu) { 85 | // Inflate the menu; this adds items to the action bar if it is present. 86 | getMenuInflater().inflate(R.menu.my, menu); 87 | return true; 88 | } 89 | 90 | @Override 91 | public boolean onOptionsItemSelected(MenuItem item) { 92 | // Handle action bar item clicks here. The action bar will 93 | // automatically handle clicks on the Home/Up button, so long 94 | // as you specify a parent activity in AndroidManifest.xml. 95 | int id = item.getItemId(); 96 | 97 | //noinspection SimplifiableIfStatement 98 | if (id == R.id.action_settings) { 99 | return true; 100 | } 101 | 102 | return super.onOptionsItemSelected(item); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/NameFragment.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.app.Dialog; 7 | import android.app.DialogFragment; 8 | import android.content.DialogInterface; 9 | import android.os.Bundle; 10 | import android.widget.Toast; 11 | 12 | public class NameFragment extends DialogFragment { 13 | 14 | public static interface Rateable { 15 | void modifyRating(String name, int amount); 16 | } 17 | 18 | private Rateable rater; 19 | 20 | @Override 21 | public void onAttach(Activity activity) { 22 | super.onAttach(activity); 23 | rater = (Rateable) activity; 24 | } 25 | 26 | @Override 27 | public Dialog onCreateDialog(Bundle savedInstanceState) { 28 | final String name = getArguments().getString("name"); 29 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 30 | builder.setTitle(R.string.alert_title) 31 | .setIcon(android.R.drawable.star_on) 32 | .setMessage(String.format("How do you feel about %s?", name)) 33 | .setPositiveButton("Positive, +1", new DialogInterface.OnClickListener() { 34 | @Override 35 | public void onClick(DialogInterface dialog, int which) { 36 | rater.modifyRating(name, 1); 37 | } 38 | }) 39 | .setNeutralButton("Neutral, 0", new DialogInterface.OnClickListener() { 40 | @Override 41 | public void onClick(DialogInterface dialog, int which) { 42 | Toast.makeText(getActivity(), name + " is going to be disappointed", 43 | Toast.LENGTH_SHORT).show(); 44 | } 45 | }) 46 | .setNegativeButton("Negative, -1", new DialogInterface.OnClickListener() { 47 | @Override 48 | public void onClick(DialogInterface dialog, int which) { 49 | rater.modifyRating(name, -1); 50 | } 51 | }); 52 | return builder.create(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/User.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.Ignore; 5 | import androidx.room.PrimaryKey; 6 | 7 | @Entity(tableName = "users") 8 | public class User { 9 | private static int nextId = 0; 10 | 11 | @PrimaryKey 12 | private int uid; 13 | 14 | private String name; 15 | 16 | @Ignore 17 | public User() { 18 | this(nextId++, "DEFAULT_NAME"); 19 | } 20 | 21 | @Ignore 22 | public User(String name) { 23 | this(nextId++, name); 24 | } 25 | 26 | public User(int uid, String name) { 27 | this.uid = uid; 28 | this.name = name; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public int getUid() { 40 | return uid; 41 | } 42 | 43 | public void setUid(int uid) { 44 | this.uid = uid; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "User{" + 50 | "uid=" + uid + 51 | ", name='" + name + '\'' + 52 | '}'; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) return true; 58 | if (o == null || getClass() != o.getClass()) return false; 59 | 60 | User user = (User) o; 61 | 62 | if (uid != user.uid) return false; 63 | return name != null ? name.equals(user.name) : user.name == null; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int result = uid; 69 | result = 31 * result + (name != null ? name.hashCode() : 0); 70 | return result; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/UserDAO.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import androidx.room.Dao; 4 | import androidx.room.Delete; 5 | import androidx.room.Insert; 6 | import androidx.room.Query; 7 | 8 | import java.util.List; 9 | 10 | @Dao 11 | public interface UserDAO { 12 | @Query("SELECT * FROM users") 13 | List getAllUsers(); 14 | 15 | @Query("SELECT EXISTS(SELECT 1 FROM users where name=:name)") 16 | boolean exists(String name); 17 | 18 | @Insert 19 | void insertUsers(User... user); 20 | 21 | @Delete 22 | void deleteUsers(User... user); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/nfjs/helloworldas/WelcomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.nfjs.helloworldas; 2 | 3 | import android.app.Activity; 4 | import androidx.room.Room; 5 | import android.os.AsyncTask; 6 | import android.app.DialogFragment; 7 | import android.app.Notification; 8 | import android.app.NotificationManager; 9 | import android.app.PendingIntent; 10 | import android.app.TaskStackBuilder; 11 | import android.content.Intent; 12 | import android.os.Bundle; 13 | import android.util.Log; 14 | import android.view.Menu; 15 | import android.view.MenuItem; 16 | import android.view.View; 17 | import android.widget.AdapterView; 18 | import android.widget.ArrayAdapter; 19 | import android.widget.ListView; 20 | import android.widget.TextView; 21 | import android.widget.Toast; 22 | 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | 29 | public class WelcomeActivity extends Activity implements NameFragment.Rateable { 30 | public static final int NOTIFICATION_ID = 314159; 31 | 32 | private TextView greetingText; 33 | private ListView listView; 34 | private Map ratings = new HashMap<>(); 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_welcome); 40 | if (getActionBar() != null) { 41 | getActionBar().setDisplayHomeAsUpEnabled(true); 42 | } 43 | 44 | String name = getIntent().getStringExtra("name"); 45 | greetingText = findViewById(R.id.greeting_text); 46 | String format = getString(R.string.greeting); 47 | greetingText.setText(String.format(format, name)); 48 | notifyUser(name); 49 | 50 | listView = findViewById(R.id.list_view); 51 | new DisplayNamesTask().execute(name); 52 | 53 | } 54 | 55 | private class DisplayNamesTask extends AsyncTask> { 56 | 57 | @Override 58 | protected List doInBackground(String... params) { 59 | AppDatabase db = Room.databaseBuilder(getApplicationContext(), 60 | AppDatabase.class, "users.db").build(); 61 | 62 | String name = params[0]; 63 | 64 | UserDAO userDao = db.getUserDao(); 65 | if (!userDao.exists(name)) { 66 | userDao.insertUsers(new User(name)); 67 | } 68 | 69 | List users = userDao.getAllUsers(); 70 | List names = new ArrayList<>(); 71 | for (User user : users) { 72 | names.add(user.getName()); 73 | } 74 | 75 | db.close(); 76 | 77 | return names; 78 | } 79 | 80 | @Override 81 | protected void onPostExecute(List names) { 82 | super.onPostExecute(names); 83 | ArrayAdapter arrayAdapter 84 | = new ArrayAdapter<>( 85 | WelcomeActivity.this, 86 | android.R.layout.simple_list_item_1, 87 | names); 88 | 89 | listView.setAdapter(arrayAdapter); 90 | 91 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 92 | @Override 93 | public void onItemClick(AdapterView parent, View view, int position, long id) { 94 | Log.d("TAG", "Item at " + position + " clicked"); 95 | String name = parent.getItemAtPosition(position).toString(); 96 | greetingText.setText( 97 | String.format(getString(R.string.greeting), 98 | name)); 99 | 100 | DialogFragment fragment = new NameFragment(); 101 | Bundle arguments = new Bundle(); 102 | arguments.putString("name", name); 103 | fragment.setArguments(arguments); 104 | fragment.show(getFragmentManager(), "Nothing"); 105 | } 106 | }); 107 | } 108 | } 109 | 110 | private void notifyUser(String name) { 111 | Intent intent = new Intent(this, MyActivity.class); 112 | TaskStackBuilder tsb = TaskStackBuilder.create(this); 113 | tsb.addParentStack(MyActivity.class); 114 | tsb.addNextIntent(intent); 115 | PendingIntent pendingIntent = 116 | tsb.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 117 | Notification notification = new Notification.Builder(this) 118 | .setSmallIcon(R.drawable.ic_launcher) 119 | .setContentTitle(getString(R.string.app_name)) 120 | .setContentText("Greeted: " + name) 121 | .setAutoCancel(true) 122 | .setTicker("Greeted: " + name) 123 | .setContentIntent(pendingIntent) 124 | .build(); 125 | 126 | NotificationManager manager = 127 | (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 128 | manager.notify(NOTIFICATION_ID, notification); 129 | } 130 | 131 | @Override 132 | public void modifyRating(String name, int amount) { 133 | if (ratings.get(name) != null) { 134 | ratings.put(name, ratings.get(name) + amount); 135 | } else { 136 | ratings.put(name, amount); 137 | } 138 | Toast.makeText(this, String.format("%s has rating %d", name, ratings.get(name)), 139 | Toast.LENGTH_SHORT).show(); 140 | } 141 | 142 | @Override 143 | protected void onDestroy() { 144 | super.onDestroy(); 145 | } 146 | 147 | @SuppressWarnings("NullableProblems") 148 | @Override 149 | protected void onSaveInstanceState(Bundle outState) { 150 | super.onSaveInstanceState(outState); 151 | outState.putString("display", greetingText.getText().toString()); 152 | 153 | String[] names = ratings.keySet().toArray(new String[ratings.keySet().size()]); 154 | outState.putStringArray("names", names); 155 | for (String name : names) { 156 | outState.putInt(name, ratings.get(name)); 157 | } 158 | } 159 | 160 | @Override 161 | protected void onRestoreInstanceState(Bundle savedInstanceState) { 162 | super.onRestoreInstanceState(savedInstanceState); 163 | greetingText.setText(savedInstanceState.getString("display")); 164 | String[] names = savedInstanceState.getStringArray("names"); 165 | for (String name : names) { 166 | ratings.put(name, savedInstanceState.getInt(name)); 167 | } 168 | } 169 | 170 | @Override 171 | public boolean onCreateOptionsMenu(Menu menu) { 172 | // Inflate the menu; this adds items to the action bar if it is present. 173 | getMenuInflater().inflate(R.menu.welcome, menu); 174 | return true; 175 | } 176 | 177 | @Override 178 | public boolean onOptionsItemSelected(MenuItem item) { 179 | // Handle action bar item clicks here. The action bar will 180 | // automatically handle clicks on the Home/Up button, so long 181 | // as you specify a parent activity in AndroidManifest.xml. 182 | int id = item.getItemId(); 183 | 184 | //noinspection SimplifiableIfStatement 185 | if (id == R.id.action_settings) { 186 | return true; 187 | } 188 | 189 | return super.onOptionsItemSelected(item); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kousen/HelloWorldAS/66dc5d1bc274a4c4955101b3b007f84432820dc8/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_my.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | 19 | 24 | 25 | 30 | 31 |