トップ・ページの表示 注意書きの表示 掲示板に書き込む前に必ず この ”注意書き”を お読み下さい.

"伊邪那"

   
   

ページの表示順:{ 新しい順/ 古い順}.
初期・ページの表示・位置:{ 先頭ページ/ 末尾ページ}.
1ページ内のスレッド表示数:











<Number>: [000009D3]  <Date>: 2016/05/08 16:46:57
<Title>: AppCompat 012
<Name>: amanojaku@管理人



ActionBarActivity が非推奨になったので、チャントした(プログラム的に) Empty(空(から)) な AppCompatActivity を作ってみる。

以前は良くワカランかったが、「getSupportActionBar( )」で1歩前進したが、それでは Toolbar の「Logo(アイコン)」の表示ができなかったので、下記「OKWAVE」で「Logo(アイコン)、Icon」の表示方法を ご教授いただきました(詳細は下記「OKWAVE」ページ参照)。
Toolbar の Icon のイベントの取得は そんなに難しくないだろうと思って、イロイロ検索しても全然ダメでした。

OKWAVE
http://okwave.jp/qa/q9115191.html


《参考》

TextViewに枠線を付ける&背景色を変更する - yokkongの日記
http://d.hatena.ne.jp/yokkong/20111116/1321425474

【Android開発】スタイルにborderがないけど枠線を表示したい
http://se-suganuma.blogspot.jp/2010/02/androidborder.html


とりあえず、[New Project]で(1ページ目)[Application name]:「AppCompat」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。

『<Project名>→app→main→res→drawable-hdpi』フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、「アプリの Logo マーク」を Android のマスコット・キャラの「Droid君」にしている。
下の方に『<Project名>→app→main→res→drawable-hdpi』フォルダー用の画像ファイル「アプリの Logo マーク:「48x48」Pixel、その他"TextView"用アイコンをアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイルを作成してやれば良い。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。


『<Project名>→app→main→java→com.example.appcompat→MainActivity(MainActivity.java)』


package com.example.appcompat;

import android.os.Bundle;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.os.Environment;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.Context;
import android.content.res.Resources;
import android.content.Intent;
import android.app.PendingIntent;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.util.Log;

import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 012";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    DisplayMetrics oDisplayMetrics;
    public static int iPhysicalWidth, iPhysicalHeight;
    float vfDisplayMetricsDensity;

    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext; // Application Context.
    Context oContext; // Activity Context.
    String vsToolbarTitle;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;

    TextView wtvMessage;
    static final int[] WIDGETS =  new int[]{
            R.id.textView1, R.id.textView2,
            R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1 };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (DEBUG) Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext();
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext();
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;
        oDisplayMetrics = oAppContext.getResources( ).getDisplayMetrics( );
        vfDisplayMetricsDensity = oDisplayMetrics.density;
        iPhysicalWidth = oDisplayMetrics.widthPixels;
        iPhysicalHeight = oDisplayMetrics.heightPixels;
        if (DEBUG) Log.i(tag, "Activity.onCreate( ) : " +
                "vfDisplayMetricsDensity=" + vfDisplayMetricsDensity + "; " +
                "iPhysicalWidth=" + iPhysicalWidth + "; " +
                "iPhysicalHeight=" + iPhysicalHeight + "; " +
                "");

        vsPackageName = getPackageName();
        try {
            PackageInfo packageInfo = getPackageManager().getPackageInfo(
                    getPackageName(), PackageManager.GET_META_DATA);
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }

        wtvMessage = (TextView) findViewById(R.id.wtvMessage);
        wtvMessage.append("fsDebugCrest=" + fsDebugCrest + ";\n" +
                "PackageName=" + vsPackageName + ";\n" +
                "VersionName=" + vsVersionName + ";\n" +
                "RevisionNo=" + String.valueOf(iVersionCode) + ";\n" +
                "vfDisplayMetricsDensity=" + vfDisplayMetricsDensity + ";\n" +
                "iPhysicalWidth=" + iPhysicalWidth + ";\n" +
                "iPhysicalHeight=" + iPhysicalHeight + "\n" +
                "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionbar = getSupportActionBar();

        // actionbar.setIcon(R.drawable.ic_menu_info_details);
        actionbar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  " + getString(R.string.app_name) +
                fsDebugCrest +
                " V" + vsVersionName + // Version.
                " R" + String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        actionbar.setTitle(vsToolbarTitle);

        for (int iWId : WIDGETS) {
            View wvw = findViewById(iWId);
            wvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        if (DEBUG) Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (DEBUG) Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (DEBUG) Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (DEBUG) Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (DEBUG) Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (DEBUG) Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case  R.id.textView1 :
                if (DEBUG) Log.i(tag, "textView1");

                break;
            case  R.id.textView2 :
                if (DEBUG) Log.i(tag, "textView2");

                break;
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if (DEBUG) Log.i(tag, "Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch (keyCode) {
            case KeyEvent.KEYCODE_MENU:
                if (DEBUG) Log.i(tag, "KeyEvent.KEYCODE_MENU;");
                if (action == KeyEvent.ACTION_UP) {
                    if (DEBUG) Log.i(tag, "KeyEvent.ACTION_UP;");
                    if (oMainMenu != null) {
                        if (DEBUG) Log.i(tag, "oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default:
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「UI Widget」の枠として使用する。


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→drawable→button_style.xml』(ファイルを作成)
※「TextView」をボタン・スタイルで表示させている。


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/ButtonBackground"/>
            <corners android:radius="3dp" />
            <stroke android:width="2dp" android:color="@color/ButtonShadow" />
            <!-- 「stroke android:width=」は ずらす幅より少し大きな値にする。 -->
        </shape>
    </item>
    <item android:bottom="1.45dp" android:right="1.47dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/ButtonBackground"/>
            <padding android:top="4.4dp" android:bottom="4.4dp"
                android:left="5dp" android:right="5dp" />
            <corners android:radius="3dp" />
        </shape>
    </item>
</layer-list>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="@color/Background_Cornflowerblue" />

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_margin = "7dp"
        android:background="@drawable/border" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="top"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:id="@+id/wtvMessage" />

    </ScrollView>

<!-- ボタンは左右にムダなスペースがあるので、表示幅が Tight でボタンが表示できない場合、-->
<!-- TextView をボタン・スタイルで表示させれば良い。 -->
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:textColor="@color/black"
            android:layout_marginTop = "6dp"
            android:layout_marginBottom = "6dp"
            android:layout_marginLeft = "3.5dp"
            android:layout_marginRight = "3.5dp"
            android:background="@drawable/button_style"
            android:text="TextView1"
            android:id="@+id/textView1" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:textColor="@color/black"
            android:layout_marginTop = "6dp"
            android:layout_marginBottom = "6dp"
            android:layout_marginLeft = "3.5dp"
            android:layout_marginRight = "3.5dp"
            android:background="@drawable/button_style"
            android:drawableRight="@drawable/xic_menu_moreoverflow_focused_holo_light0272"
            android:text="TextView2"
            android:id="@+id/textView2" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:text="Button1"
            android:id="@+id/button1" />

        <ToggleButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:textSize="22dp"
            android:text="ToggleButton1"
            android:id="@+id/toggleButton1" />
    </LinearLayout>

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="22dp"
        android:text="CheckBox1"
        android:id="@+id/checkBox1" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="22dp"
        android:layout_gravity="center_horizontal"
        android:text="RadioButton1"
        android:id="@+id/radioButton1" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→colors.xml』


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="holo_blue_light">#33b5e5</color>
    <color name="Background_Cornflowerblue">#6495ED</color>
    <color name="Background_MediumslateblueLike">#9b7fff</color>

    <color name="ButtonBackground">#d7d7d7</color>
    <color name="ButtonShadow">#c6c6c6</color>
    <color name="GrayOutText001">#909090</color>
    <color name="GrayOutText002">#888888</color>
    <color name="GrayOutText003">#777777</color>
    <color name="SettingMenuBackground">#eeeeee</color>

    <color name="black">#000000</color>
    <color name="gray">#808080</color>
    <color name="silver">#c0c0c0</color>
    <color name="white">#ffffff</color>
    <color name="maroon">#800000</color>
    <color name="red">#ff0000</color>
    <color name="purple">#800080</color>
    <color name="fuchsia">#ff00ff</color>
    <color name="magenta">#ff00ff</color>
    <color name="green">#008000</color>
    <color name="lime">#00ff00</color>
    <color name="olive">#808000</color>
    <color name="yellow">#ffff00</color>
    <color name="navy">#000080</color>
    <color name="blue">#0000ff</color>
    <color name="teal">#008080</color>
    <color name="aqua">#00ffff</color>
    <color name="cyan">#00ffff</color>

    <color name="pink">#ffc0cb</color>
    <color name="orange">#ffa500</color>
    <color name="greenyellow">#adff2f</color>
    <color name="yellowgreen">#9acd32</color>
</resources>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.enter.appcompat">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity"
            android:launchMode="singleInstance" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


<Number>: [000009E1]  <Date>: 2016/03/20 06:31:02
<Title>: WidgetELSProperty 009
<Name>: amanojaku@管理人



Widget(今回は Service 内)で View コンポーネントの Property を動的に変更する。
クリックにより(Service の) onStartCommand が実行されたら、TextView の Background Color を Random に変更する。
Widget のメソッド実行時に設定された(Widget Class 内の) Static 変数は、そのメソッドが実行中なら、当然 その値は保持されているだろうが、その後 その(Widget Class 内の) Static 変数の値が消失しているような感じなので、Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無いようだ、当然 Service 起動時に その(Widget Class 内の) Static 変数の値が残っている保障など無い。
よって Widget から Service へのデータの受け渡しには Wgeidt Class 内の Static 変数は使えない(Widget → Service の一方通行のデータの受け渡しに限定するなら、Intent により非常に簡単にデータを受け渡せるようだ、それに関しては次の WidgetILSToast にて記す)。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELSProperty」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/


『<Project名>→app→main→java→com.example.widgetelsproperty→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelsproperty;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// WidgetELSProperty(Widget:{(Event Launch Service)(click)[Property]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 009";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    // Android Java の場合、カラーの実態は int。
    static final int[] d1iCPalette = new int[]{
            Color.BLUE,
            Color.CYAN,
            Color.GREEN,
            Color.MAGENTA,
            Color.RED,
            Color.YELLOW,
    };

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName( ), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        Intent si = new Intent(ctx, MyService.class);
        PendingIntent pi = PendingIntent.getService(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");
            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName( ), R.layout.widget_main);

            // Android Java の場合、カラーの実態は int。
            int iColor = d1iCPalette[(int)(Math.random( ) * d1iCPalette.length)];
            rv.setInt(R.id.ctvTextView, "setBackgroundColor", iColor);
            String vsMessage = "MyService.onStartCommand( );\n"+
                    "iColor="+iColor+";\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( ) : "+
                    "iColor="+iColor+"; "+
                    "");

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#c0c0c0"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#000000"
    android:editable="false" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetelsproperty">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [000009E3]  <Date>: 2016/03/20 06:33:05
<Title>: WidgetILSToast 020
<Name>: amanojaku@管理人



どうも Widget から直接 Toast を使えないようで、Widget で Toast を表示したい場合は Service から Toast を表示させてやれば良いようだ。
また Widget のメソッド実行時に設定された(Widget Class 内の) Static 変数は、そのメソッドが実行中なら、当然 その値は保持されているだろうが、その後 その(Widget Class 内の) Static 変数の値が消失しているような感じなので、Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無いようだ、当然 Service 起動時に その(Widget Class 内の) Static 変数の値が残っている保障など無い。
よって Widget から Service へのデータの受け渡しには Wgeidt Class 内の Static 変数は使えない(Widget → Service の一方通行のデータの受け渡しに限定するなら、Intent により非常に簡単にデータを受け渡せる。
このプログラムで Intent によって Widget から Service にデータを受け渡すサンプルも記してみる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSToast」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&


『<Project名>→app→main→java→com.example.widgetilstoast→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilstoast;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.view.Gravity;
import android.widget.RemoteViews;
import android.util.Log;
import android.widget.Toast;

// WidgetILSToast(Widget:{(Instant Launch Service)[Toast]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 020";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "AppWidget.onUpdate( );\n";
        si.putExtra("MessageKey", vsMessage);
        si.putExtra("ToastKey", "Hello, world!");
        ctx.startService(si);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = si.getStringExtra("MessageKey");
            if( null==vsMessage ){
                vsMessage =  "fsDebugCrest="+fsDebugCrest+";\n";
            }
            vsMessage = vsMessage+
                    "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            String vsToast = si.getStringExtra("ToastKey");
            if( null==vsToast ){
                vsToast =  "onStartCommand( )";
            }
            Toast.makeText(getApplicationContext( ), vsToast, Toast.LENGTH_SHORT).show();

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetilstoast">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [000009F0]  <Date>: 2016/03/20 06:35:13
<Title>: WidgetELActivity 003
<Name>: amanojaku@管理人



Widget から Activity を起動。
以前 作ったモノを元に AppCompat を起動してみる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELActivity」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
「drawable-mdpi、drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_info_details.png、ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

「drawable-mdpi、drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、アプリの Logo マークを Android のマスコット・キャラの「Droid君」にしている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(「48x48」Pixel)をアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)を作成してやれば良い。
「Toolbar」の UI はスタンダードな「ActionBarActivity」のような感じになっている。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&


『<Project名>→app→main→java→com.example.widgeteactivity→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelactivity;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// WidgetELActivity(Widget:{(Event Launch Activity)(click)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 003";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        // 起動するアクティビティの指定
        Intent si = new Intent(ctx, MainActivity.class);
        PendingIntent pi = PendingIntent.getActivity(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent intent, int f, int sid) {
            super.onStartCommand(intent, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");
            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent intent) {
            // super.onBind(intent);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→java→com.example.widgetelactivity→MainActivity(MainActivity.java)』


package com.example.widgetelactivity;

import android.os.Bundle;
import android.util.Log;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.content.Context;

import android.view.View;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.widget.TextView;
import android.view.KeyEvent;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 008";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。
    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext;
    Context oContext;
    ActionBar oActionBar;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;
    static final int[] WIDGETS =
            new int[]{R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1};
    TextView ctvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(DEBUG)Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext( );
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext( );
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName( );
        try {
            PackageInfo packageInfo = this.getPackageManager( ).getPackageInfo(
                    this.getPackageName( ), PackageManager.GET_META_DATA );
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace( );
        }

        ctvMessage = (TextView)findViewById(R.id.message);
        ctvMessage.append(
                "PackageName=" + vsPackageName + ";\n" +
                        "VersionName=" + vsVersionName + ";\n" +
                        "BuildNo=" + String.valueOf(iVersionCode) + ";\n" +
                        "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        oActionBar = getSupportActionBar();

        // oActionBar.setIcon(R.drawable.ic_menu_info_details);
        oActionBar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  "+getString(R.string.app_name)+
                fsDebugCrest+
                " V"+vsVersionName+ // Version.
                " R"+String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        oActionBar.setTitle(vsToolbarTitle);

        for (int iWgId : WIDGETS) {
            View cvw = findViewById(iWgId);
            cvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart( ) {
        super.onRestart();
        if(DEBUG)Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart( ) {
        super.onStart();
        if(DEBUG)Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume( ) {
        super.onResume();
        if(DEBUG)Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause( ) {
        super.onPause();
        if(DEBUG)Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop( ) {
        super.onStop();
        if(DEBUG)Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(DEBUG)Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if(DEBUG) Log.i(tag,"Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch(keyCode) {
            case KeyEvent.KEYCODE_MENU :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_MENU;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
                    if ( oMainMenu!=null ) {
                        if(DEBUG) Log.i(tag,"oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default :
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「TextView」の枠として使っている。


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <TextView
        android:id="@+id/message"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
        android:textSize="@dimen/abc_text_size_medium_material"
        android:layout_weight="1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        android:id="@+id/button1"
        android:layout_gravity="center_horizontal" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ToggleButton 1"
        android:id="@+id/toggleButton1"
        android:layout_gravity="center_horizontal" />

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox 1"
        android:id="@+id/checkBox1"
        android:layout_gravity="center_horizontal" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RadioButton 1"
        android:id="@+id/radioButton1"
        android:layout_gravity="center_horizontal" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』
※この場合 <activity>タグに"launchMode"は設定できないようだ、この場合 「launchMode="singleInstance"」を設定しなくても Activity のインスタンスは1つだけに限定されるようだ。


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetelactivity">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <receiver
            android:name=".AppWidget"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <activity android:name=".MainActivity" >
        </activity>
    </application>

</manifest>


<Number>: [000009FA]  <Date>: 2016/04/30 15:22:37
<Title>: WidgetELSStorage 012
<Name>: amanojaku@管理人



「Widget、Service」から Storage に読み書きする。
今回は「内部ストレージのアプリ固有の領域」に対して Stream を読み書きしてみる。
Widget の onEnabled 実行時に Stream のデータを消去。
Widget の onUpdate 実行時に、onUpdate イベントのログを Stream に追加する。
クリックによる Service の onStartCommand 実行時に、onStartCommand イベントのログを Stream に追加する。
なお、「内部ストレージのアプリ固有の領域」は(セキュリティー上)ファイラーからは見えないようになってるらしい。
ちなみに「外部ストレージ領域」はファイラーから見えるので、「パスワード、個人情報」などのようなデータは「外部ストレージ領域」に保存してはならない。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetELSStorage」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

アプリからデータを内部&外部ストレージへの保存
http://androidadvent.blog.shinobi.jp/2012/internal-external-storage-save

AndroidのContextについて
http://qiita.com/tyfkda/items/bbb77a0e5a97eda9d4b6

> getApplicationContext()
> getBaseContext()

> どれを使うべきか、はたまたgetApplicationで取得できるApplicationもContextを継承していて、どれをつかったらいいの?と悩む。

> Activityはアクティビティ単位なので、別のアクティビティに遷移するとContextも変わる
> Applicationは起動中のアプリに対して1個存在して、Contextも1つ。アプリが生きている間存在するアクティビティが変わっても同じ内容
> getBaseContextは別のアプリとやりとりするとき用

> OSからみてアプリケーションの機能として見ることができるモノ(SharedPreferencesやDB,リソースへのアクセスなど)はApplicationContextを用い,Activityに依存するモノ(BroadcastReceiverやViewのインスタンス生成など)はActivityを用いるもの

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&


『<Project名>→app→main→java→com.example.widgetelsstorage→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetelsstorage;

import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.io.IOException;

import java.util.List;
import java.util.ArrayList;

import java.text.SimpleDateFormat;
import java.util.Date;

// WidgetELSStorage(Widget:{(Event Launch Service)(click)[Storage]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 012";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final String fvsSerialFile = "Storage.dat";
    static final SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    // Android Java の場合、カラーの実態は int。
    static final int[] d1iCPalette = new int[]{
            Color.BLUE,
            Color.CYAN,
            Color.GREEN,
            Color.MAGENTA,
            Color.RED,
            Color.YELLOW,
    };

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

        Context oAppContext = ctx.getApplicationContext( );
        // 初期化。
        List<String> dl1Event = new ArrayList<String>( );
        Write(oAppContext, dl1Event);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        Context oAppContext = ctx.getApplicationContext( );
        List<String> dl1Event = new ArrayList<String>( );
        Read(oAppContext, dl1Event);
        dl1Event.add("AppWidget.onUpdate");
        Write(oAppContext, dl1Event);

        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName( ), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        Intent si = new Intent(ctx, MyService.class);
        PendingIntent pi = PendingIntent.getService(ctx, 0, si, 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");
    }

    static void Read(Context oAppContext, List<String> dl1Event) {
        if(DEBUG)Log.i(tag, "AppWidget.Read( );");
        try {
            File vsFFDir = oAppContext.getFilesDir();
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "vsFFDir.getCanonicalPath()="+vsFFDir.getCanonicalPath()+"; "+
                    "");
            File vsFSPath = oAppContext.getFileStreamPath(fvsSerialFile);
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "vsFSPath.getCanonicalPath()="+vsFSPath.getCanonicalPath()+"; "+
                    "");
            FileInputStream oFIS = oAppContext.openFileInput(fvsSerialFile);
            BufferedInputStream oBIS = new BufferedInputStream(oFIS);
            ObjectInputStream oOISSerial = new ObjectInputStream(oBIS);
            int iListSize = ((Integer)oOISSerial.readObject( )).intValue( );
            if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                    "iListSize="+iListSize+"; "+
                    "");
            for( int i= 0; i<iListSize; i++ ){
                dl1Event.add((String)oOISSerial.readObject( ));
                if(DEBUG)Log.i(tag,"AppWidget.Read( ) : "+
                        "dl1Event.get(i)="+dl1Event.get(i)+"; "+
                        "");
            }
            oOISSerial.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void Write(Context oAppContext, List<String> dl1Event) {
        if(DEBUG)Log.i(tag, "AppWidget.Write( );");
        try {
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "dl1Event.size( )="+dl1Event.size( )+"; "+
                    "");
            File vsFFDir = oAppContext.getFilesDir();
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "vsFFDir.getCanonicalPath()="+vsFFDir.getCanonicalPath()+"; "+
                    "");
            // Context の openFileOutput は内部ストレージのアプリ固有の領域にアクセスされるらしい。
            // Context.MODE_PRIVATE:PRIVATE 出力モード:他のアプリからはアクセスできない。
            FileOutputStream oFOS = oAppContext.openFileOutput(fvsSerialFile, Context.MODE_PRIVATE);
            BufferedOutputStream oBOS = new BufferedOutputStream(oFOS);
            ObjectOutputStream oOOSSerial = new ObjectOutputStream(oBOS);
            oOOSSerial.writeObject(new Integer(dl1Event.size()));
            for( int i = 0; i<dl1Event.size( ); i++ ){
                oOOSSerial.writeObject(dl1Event.get(i));
            }
            oOOSSerial.close();
            File vsFSPath = oAppContext.getFileStreamPath(fvsSerialFile);
            if(DEBUG)Log.i(tag,"AppWidget.Write( ) : "+
                    "vsFSPath.getCanonicalPath()="+vsFSPath.getCanonicalPath()+"; "+
                    "");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            String vsDateTime = oDateFormat.format(
                    new Date(System.currentTimeMillis()));
            if (DEBUG) Log.i(tag, "AppWidget : " +
                    "vsDateTime=" +vsDateTime+ "; " +
                    "");
            Context oAppContext = getApplicationContext();
            List<String> dl1Event = new ArrayList<String>( );
            Read(oAppContext, dl1Event);
            dl1Event.add("MyService.onStartCommand : "+
                    "vsDateTime=" +vsDateTime);
            Write(oAppContext, dl1Event);

            // 前述の Widget の場合の記述とビミョウに違っている部分も有るので要注意。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName( ), R.layout.widget_main);

            // Android Java の場合、カラーの実態は int。
            int iColor = d1iCPalette[(int)(Math.random( ) * d1iCPalette.length)];
            rv.setInt(R.id.ctvTextView, "setBackgroundColor", iColor);
            String vsMessage = "MyService.onStartCommand( );\n"+
                    "iColor="+iColor+";\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( ) : "+
                    "iColor="+iColor+"; "+
                    "");

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#c0c0c0"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#000000"
    android:editable="false" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetelsstorage">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [00000A05]  <Date>: 2016/04/16 00:43:37
<Title>: WidgetILSTimerILSBKill 006
<Name>: amanojaku@管理人



Widget で(Service から) Timer を起動、Broadcast による通知で Timer を停止する。
秒単位での短い間隔の繰り返しには Timer オブジェクトが適してるらしいので、Timer オブジェクトを使用する。
Widget Class 内のフィールド変数ではデータが保持されないので、Timer オブジェクトを Service Class 内のフィールド変数として定義し、Service から起動する。
Broadcast で Timer に通知して停止させる、良く分からないが Widget だと「getBaseContext( )」が使えないようで、それだと Broadcast できないと言うことになるから、結局 (Broadcast に関しては) Service から実行する必要があるようだ。
問題点としては このプログラムでは Timer を1つしか作れないので、複数の Timer が必要な場合は何らかの工夫が必要となる。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSTimerILSBKill」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&


『<Project名>→app→main→java→com.example.widgetilstimerilsbkill→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilstimerilsbkill;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.util.Timer;

// WidgetILSTimerILSBKill(Widget:{(Instant Launch Service)[Timer],(Instant Launch Service)[Broadcast Kill]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 006";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

// (世界中で一意の「Intent Filter 名」とするため)「Intent Filter」に設定する文字列は
// パッケージ名「自分のサイトのドメイン名を逆転させた名前+アプリケーション名」を設定する事が推奨されている。
// ここではパッケージ名 自体が手抜きなので とりあえず
// 「国名+自分の大まかな出生地+ハンドル・ネーム+アプリケーション名」としている
// (出生地にしておけば引っ越しても変更する必要がない)。
// これは あくまで暫定的な一例であり、実際の「Intent Filter 名」は もっとチャントしたモノが良いだろう。
// もし、ここの「Intent Filter」を変更した場合は
// 「AndroidManifest.xml」の「Intent Filter 名」も変更しなければならない。
// 本来、「パッケージ名」自体が もっとチャントしたモノになっていれば、
// 「getPackageName( )」を使えば良いだけなだが…。
// このプログラムでは、この「fsIntentFilter_Action」変数に「null」を設定すれば、
// 「getPackageName( )」の値が使用されるようになっている。
// その場合も 当然「AndroidManifest.xml」の「Intent Filter 名」を変更しなければならない。
// ただイキナリ、世界中で一意の「Intent Filter 名」を考え出すのも難しいかもしれないので、
// とりあえず このような変数を使って試行錯誤すると言う手もある
// (試行錯誤する場合、いちいち「パッケージ名」自体を変更するのは面倒)。
    static final String fsIntentFilter_Action = "jp.kanagawa.amanojaku.WidgetILSTimerILSBKill";

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

// サービスの起動を1回だけに限定したい場合は、サービスの起動を onEnabled メソッド内に記述する。
        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        si.putExtra("KillKey", Boolean.valueOf(true)); // false; //
        ctx.startService(si);
        /*
        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
        */
    }

    public static class MyService extends Service {

        Timer timer; // = new Timer( );

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Boolean oKill = si.getBooleanExtra("KillKey", false);
            if( ! oKill.booleanValue( ) ){
                if(DEBUG)Log.i(tag, "MyTimerTask( );");
                long delay = 0; // 0ms
                long interval = 1000; // 1000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer), delay, interval);
            }else{
                if(DEBUG)Log.i(tag, "Kill;");
                String ifa = fsIntentFilter_Action;
                if( null==ifa ){
                    ifa = getPackageName( );
                }
                Intent bi = new Intent( );
                bi.setAction(ifa);
                getBaseContext( ).sendBroadcast(bi);
                stopSelf( );
            }

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→java→com.example.widgetilstimerilsbkill→MyTimerTask(MyTimerTask.java)』(ファイルを作成)


package com.example.widgetilstimerilsbkill;

import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyTimerTask extends TimerTask {
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = AppWidget.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static final String fsIntentFilter_Action = AppWidget.fsIntentFilter_Action;

    static Timer timer; // = new Timer();
    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    public MyTimerTask(Timer t){
        super();

        timer = t;
    }

    public static class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context ctx, Intent ri) {
            if (DEBUG) Log.i(tag, "Receiver.onReceive( );");

            String ifa = fsIntentFilter_Action;
            if( null==ifa ){
                ifa = ctx.getPackageName();
            }
// プログラム側と「AndroidManifest.xml」の「Intent Filter 名」が一致してないような
// ヒューマン・エラー対策として Action を比較する。
            if( ifa.equals(ri.getAction( )) ) {
                if (DEBUG) Log.i(tag, "Action;");
                if(timer != null){
                    timer.cancel( );
                    timer = null;
                }
            }
        }

    }

    @Override
    public void run( ) {
        String vsDateTime = oDateFormat.format(
                new Date(System.currentTimeMillis()));
        if (DEBUG) Log.i(tag, "TimerTask : " +
                "vsDateTime=" +vsDateTime+ "; " +
                "");
    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetilstimerilsbkill">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <receiver android:name=".MyTimerTask$MyReceiver" >
            <intent-filter>
                <action android:name="jp.kanagawa.amanojaku.WidgetILSTimerILSBKill" />
            </intent-filter>
        </receiver>
    </application>

</manifest>


<Number>: [00000A0E]  <Date>: 2016/04/16 00:46:26
<Title>: WidgetILSMTimerISLSBKill 010
<Name>: amanojaku@管理人



Widget で(Service から) 複数の Timer を起動、Broadcast による通知で Timer を停止する。
秒単位での短い間隔の繰り返しには Timer オブジェクトが適してるらしいので、ここでも Timer オブジェクトを使用する。
Widget Class 内のフィールド変数ではデータが保持されないので、Timer オブジェクトを Service Class 内のフィールド変数として定義し、Service から起動する。
Broadcast で Timer に通知して停止させる、良く分からないが Widget だと「getBaseContext( )」が使えないようで、それだと Broadcast できないと言うことになるから、結局 (Broadcast に関しては) Service から実行する必要があるようだ。
このプログラムでは MyTimerTask は Abstract(抽象) Class なので、(このプログラムで記されているとおり)必ず MyTimerTask を継承して Class を記述しなければならない。
このプログラムでは「"Alpha"、"Beta"」の2つの Timer を生成しており、Widget の起動から10秒後に Timer "Alpha"を停止させている。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

とりあえず、[New Project]で(1ページ目)[Application name]:「WidgetILSMTimerISLSBKill」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい。
現状でインストールできる「API Level 13」以下の SDK Version の最大値は「API Level 10」(Android OS 2.3.3)となり、当方も「API Level 10」(Android OS 2.3.3)をインストールしている、よって targetSdkVersion の値は「10」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※必ず(Android Studio からではなく)オリジナルの「Android SDK Manager(SDK Manager.exe)」から「API Level 10」(Android OS 2.3.3)をインストールして下さい。
「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&


『<Project名>→app→main→java→com.example.widgetilsmtimerislsbkill→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widgetilsmtimerislsbkill;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

import java.util.Timer;
import java.text.SimpleDateFormat;
import java.util.Date;

// Widget_ILSMTimer_ISLSBKill:(Widget/{(Instant Launch Service)[Multiple Timer]/({Instant,Schedule} Launch Service)[Broadcast Kill]}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 010";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    // (世界中で一意の「Intent Filter 名」とするため)「Intent Filter」に設定する文字列は
// パッケージ名「自分のサイトのドメイン名を逆転させた名前+アプリケーション名」を設定する事が推奨されている。
// ここではパッケージ名 自体が手抜きなので とりあえず
// 「国名+自分の大まかな出生地+ハンドル・ネーム+アプリケーション名」としている
// (出生地にしておけば引っ越しても変更する必要がない)。
// これは あくまで暫定的な一例であり、実際の「Intent Filter 名」は もっとチャントしたモノが良いだろう。
// もし、ここの「Intent Filter」を変更した場合は
// 「AndroidManifest.xml」の「Intent Filter 名」も変更しなければならない。
// 本来、「パッケージ名」自体が もっとチャントしたモノになっていれば、
// 「getPackageName( )」を使えば良いだけなだが…。
// このプログラムでは、この「fsIntentFilter_Action」変数に「null」を設定すれば、
// 「getPackageName( )」の値が使用されるようになっている。
// その場合も 当然「AndroidManifest.xml」の「Intent Filter 名」を変更しなければならない。
// ただイキナリ、世界中で一意の「Intent Filter 名」を考え出すのも難しいかもしれないので、
// とりあえず このような変数を使って試行錯誤すると言う手もある
// (試行錯誤する場合、いちいち「パッケージ名」自体を変更するのは面倒)。
    static final String fsIntentFilter_Action = "jp.kanagawa.amanojaku.WidgetILSMTimerISLSBKill";

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");

// サービスの起動を1回だけに限定したい場合は、サービスの起動を onEnabled メソッド内に記述する。
        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "onUpdate( );\n";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

// Timer の名前を指定してない場合は、現在 生存している Timer を全て Kill し、
// Service も終了させている。
        Intent si = new Intent(ctx, MyService.class);
        si.putExtra("KillKey", Boolean.valueOf(true)); // false; //
        ctx.startService(si);
        /*
        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
        */
    }

    public static class MyService extends Service {

        // 下記「timer、delay、interval」は あくまでもテンポラリー。
        Timer timer; // = new Timer( );
        long delay; // ms
        long interval; // ms

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "MyService.onStartCommand( );\n";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Boolean oKill = si.getBooleanExtra("KillKey", false);
            if( ! oKill.booleanValue( ) ){
                if(DEBUG)Log.i(tag, "MyTimerTask( );");

                delay = 0; // 0ms
                interval = 1000; // 1000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer, "Alpha") {
                    @Override
                    public void run() {
                        String vsDateTime = oDateFormat.format(
                                new Date(System.currentTimeMillis()));
                        if (DEBUG) Log.i(tag, "TimerTask : " +
                                "vsName=" + vsName + "; " +
                                "vsDateTime=" + vsDateTime + "; " +
                                "");
                    }
                }, delay, interval);

                delay = 0; // 0ms
                interval = 2000; // 2000ms
                timer = new Timer( );
                timer.scheduleAtFixedRate(new MyTimerTask(timer, "Beta") {
                    @Override
                    public void run() {
                        String vsDateTime = oDateFormat.format(
                                new Date(System.currentTimeMillis( )));
                        if (DEBUG) Log.i(tag, "TimerTask : " +
                                "vsName=" + vsName + "; " +
                                "vsDateTime=" + vsDateTime + "; " +
                                "");
                    }
                }, delay, interval);

// 10秒後に自分自身(MyService.onStartCommand())を呼び出して Timer "Alpha"を Kill する。
                delay = 10*1000L; // 10s
                Intent asi = new Intent(this, this.getClass( ));
                asi.putExtra("TimerKey", "Alpha");
                asi.putExtra("KillKey", Boolean.valueOf(true)); // false; //
                PendingIntent aspi = PendingIntent.getService(
                        this, 0, asi, 0);
                AlarmManager am = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.RTC, System.currentTimeMillis( )+delay, aspi);
            }else{
                if(DEBUG)Log.i(tag, "Kill;");
                String vsTimer = si.getStringExtra("TimerKey");
                String ifa = fsIntentFilter_Action;
                if( null==ifa ){
                    ifa = getPackageName( );
                }
                Intent bi = new Intent( );
                bi.setAction(ifa);
                bi.putExtra("TimerKey", vsTimer);
                getBaseContext( ).sendBroadcast(bi);
                if( null==vsTimer || "".equals(vsTimer) ){
                    stopSelf( );
                }
            }

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
        }

    }

}



『<Project名>→app→main→java→com.example.widgetilsmtimerislsbkill→MyTimerTask(MyTimerTask.java)』(ファイルを作成)


package com.example.widgetilsmtimerislsbkill;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Date;

public abstract class MyTimerTask extends TimerTask {
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = AppWidget.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static final String fsIntentFilter_Action = AppWidget.fsIntentFilter_Action;

    // static Timer timer; // = new Timer();
    // int iID = -1;
    String vsName = "";
    // static List<String> di1vsName; // = new ArrayList<String>( );
    // static List<Timer> di1oTimer; // = new ArrayList<Timer>( );
    static HashMap<String, Timer> dm1oTimer; // = new HashMap<String, Timer>( );

    SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    public MyTimerTask(Timer t, String vsN){
        super();
// Timer の同名の多重登録はチェックしていません。
        vsName = vsN;

        if( null==dm1oTimer ){
            dm1oTimer = new HashMap<String, Timer>( );
        }
        dm1oTimer.put(vsName, t);
        if(DEBUG)Log.i(tag, "MyTimerTask( ) : "+
                "vsName="+vsName+"; "+
                "dm1oTimer.size( )="+dm1oTimer.size( )+"; "+
                "");
    }

    public static class MyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context ctx, Intent ri) {
            if (DEBUG) Log.i(tag, "Receiver.onReceive( );");

            String ifa = fsIntentFilter_Action;
            if( null==ifa ){
                ifa = ctx.getPackageName();
            }
// プログラム側と「AndroidManifest.xml」の「Intent Filter 名」が一致してないような
// ヒューマン・エラー対策として Action を比較する。
            if( ifa.equals(ri.getAction( )) ) {
                if (DEBUG) Log.i(tag, "Action;");
                if( null!=dm1oTimer ){
                    Timer timer;
                    String vsTimer = ri.getStringExtra("TimerKey");
                    if( null!=vsTimer && ! vsTimer.equals("") ){
                        timer = dm1oTimer.get(vsTimer);
                        if( null!=timer ){
                            timer.cancel();
                            dm1oTimer.remove(vsTimer);
                        }
                    }else {
                        String k;
                        String[] key = dm1oTimer.keySet().toArray(new String[0]);
                        for (int i = 0; i < key.length; i++) {
// ↑どうも Android では「for( String k : dm1oTimer.keySet( ) )」のような「拡張 for 文」や、
// Iterator なども実行時エラーになるようだ。
                            k = key[i];
                            if (DEBUG) Log.i(tag, "Receiver.onReceive( ) : " +
                                    "i=" + i + "; " +
                                    "k=" + k + "; " +
                                    "");
                            timer = dm1oTimer.get(k);
                            if (null != timer) {
                                timer.cancel();
                                dm1oTimer.remove(k);
                            }
                        }
                    }
                }
            }
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widgetilsmtimerislsbkill">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <receiver android:name=".MyTimerTask$MyReceiver" >
            <intent-filter>
                <action android:name="jp.kanagawa.amanojaku.WidgetILSMTimerISLSBKill" />
            </intent-filter>
        </receiver>
    </application>

</manifest>


<Number>: [00000A19]  <Date>: 2016/04/12 02:40:49
<Title>: Widget_ILFS_Toast
<Name>: amanojaku@管理人



(Widget から起動している) Service を Foreground に設定してやり、Service を死ににくくする。
Service を Foreground に設定すると、(通知バーには表示されない場合があるが、通知バーを下ろした)通知領域にアイコンが表示される、場合によっては通知バーにもアイコンが表示される(良く分からないが そう言う仕様なのか?)。
画面から Widget が削除されると、 Service も終了されるが、その時に Foreground も解除されるので、通知領域のアイコンも消える。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

Service が実行されると Toast を表示する。
通知領域のアイコンをタップしても Service が実行され Toast が表示される。

とりあえず、[New Project]で(1ページ目)[Application name]:「Widget_ILFS_Toast」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

一応、通知用アイコンを Android のマスコット・キャラの「Droid君」にしている。
通知用アイコンは白色が推奨されているらしいので、一部 白ではないが白を基調としている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(「36x36」Pixel)をアップしてあります(「drawable-hdpi」フォルダーが存在しない場合は作成してやる)。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい、よって targetSdkVersion の値は「13」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&
WidgetILSMTimerISLSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A0E.+&


『<Project名>→app→main→java→com.example.widget_ilfs_toast→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widget_ilfs_toast;

import android.app.PendingIntent;
import android.app.Service;
import android.app.Notification;
import android.support.v7.app.NotificationCompat;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.view.Gravity;
import android.widget.RemoteViews;
import android.util.Log;
import android.widget.Toast;

// Widget_ILFS_Toast(Widget/{(Instant Launch Foreground Service)/(Toast)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 010";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final int ONGOING_NOTIFICATION = 1;

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
    }

    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate();
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

// 良く分からないが、次の PendingIntent は
// Activity の取得の場合は「getActivity(〜)」、
// Activity の取得 以外は「getService(〜)」になると思われる。
            SettingForeground(PendingIntent.getService(
                    this, 0, si, PendingIntent.FLAG_CANCEL_CURRENT));

            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                    "MyService.onStartCommand( );";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            awm.updateAppWidget(cn, rv);

            Toast.makeText(getApplicationContext( ), vsMessage, Toast.LENGTH_SHORT).show();

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        public void SettingForeground(PendingIntent pi) {
            if(DEBUG)Log.i(tag, "MyService.SettingForeground( );");
            Notification notif = new NotificationCompat.Builder(this)
                    .setContentIntent(pi)
                    .setAutoCancel(true)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("Content Text.")
                    // .setContentInfo("Content Info.")
                    // .setTicker("Ticker.")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.x_slogo_android031) // SmallIcon を設定しないと通知されない(例外も発生しない)
                    .build( );
            startForeground(ONGOING_NOTIFICATION, notif);
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
            stopForeground(true);
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widget_ilfs_toast">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher" >
        <receiver
            android:name=".AppWidget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
    </application>

</manifest>


<Number>: [00000A1E]  <Date>: 2016/04/12 08:25:11
<Title>: Widget_ILFS_NELAClick
<Name>: amanojaku@管理人



通知領域のアイコンをタップしてやると Activity を起動する(「Widget_ILFS_NELAClick、WidgetELActivity」を元に作成)。
(Widget から起動している) Service を Foreground に設定してるので、Service が死ににくくなっている。
Service を Foreground に設定すると、(通知バーには表示されない場合があるが、通知バーを下ろした)通知領域にアイコンが表示される、場合によっては通知バーにもアイコンが表示される(良く分からないが そう言う仕様なのか?)。
画面から Widget が削除されると、 Service も終了されるが、その時に Foreground も解除されるので、通知領域のアイコンも消える。
TextView のデフォルト値に Text として「A Widget isn't launched.」を設定しているので、Widget を画面に設置した時に「A Widget isn't launched.」と表示されたら、Widget が起動していない事を意味する。
しばらく待てば Widget が起動するハズだが、端末が起動中の場合やデバッグ時などは Widget が起動するまで やたら時間がかかる場合がある。
つまり、実際の Widget アプリを作成する場合も、Widget が起動してない事が判るように何かメッセージを表示するとか、(メッセージを表示できない場合は) Widget が未起動時のアイコンと起動時のアイコンを切り替えて表示するとかの工夫が必要になる場合もあるでしょう。

通知領域のアイコンをタップしてやると Activity を起動する。
Widget をタップしても Activity を起動する。

とりあえず、[New Project]で(1ページ目)[Application name]:「Widget_ILFS_NELAClick」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

「drawable-hdpi」フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、「アプリの Logo マーク、通知用アイコン」を Android のマスコット・キャラの「Droid君」にしている。
通知用アイコンは白色が推奨されているらしいので、(一部 白ではないが)白を基調としている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(アプリの Logo マーク:「48x48」Pixel、通知用アイコン:「36x36」Pixel)をアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)を作成してやれば良い。
なお、Activity の Toolbar の UI はスタンダードな「ActionBarActivity」のような感じになっている。

targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を13以下にすれば良いらしい、よって targetSdkVersion の値は「13」としている。
なお、targetSdkVersion の設定方法は仕様が変更され「<プロジェクト名>→app→build.gradle」で設定しなければならないようだ(これらを「AndroidManifest.xml」で指定しても無視されるようだ)、「build.gradle」は同名のファイルが他にもあるようなので間違えないように注意が必要。
※「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。


《参考》

Yukiの枝折: Android:Serviceの基本とonStartCommandの戻り値による ...
http://yuki312.blogspot.jp/2012/07/androidserviceonstartcommand.html

Android Widgetのライフサイクルについて要点だけ | スマートフォン要点だけブログ
http://blog.imho.jp/2011/05/android-widget.html

Androidでの常駐型Serviceを使う方法(LocalServiceによる常駐アプリ)
http://techbooster.org/android/application/3270/

AlarmService を利用したサービス実行のスケジューリング
http://android.keicode.com/basics/services-schedule-with-alarmmanager.php

WidgetELSProperty
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E1.+&
WidgetILSToast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009E3.+&
WidgetELActivity
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009F0.+&
WidgetELSStorage
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+000009FA.+&
WidgetILSTimerILSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A05.+&
WidgetILSMTimerISLSBKill
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A0E.+&
Widget_ILFS_Toast
http://artemis.rosx.net/sjis/smt.cgi?r+izanami/&bid+0000090D&tsn+00000A19.+&


『<Project名>→app→main→java→com.example.widget_ilfs_nelaclick→AppWidget(AppWidget.java)』(ファイルを作成)


package com.example.widget_ilfs_nelaclick;

import android.app.PendingIntent;
import android.app.Service;
import android.app.Notification;
import android.support.v7.app.NotificationCompat;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.content.ComponentName;
import android.widget.RemoteViews;
import android.util.Log;

// Widget_ILFS_NELAClick(appWidget/{(Instant Launch Foreground Service)/(status Notifications area):(intent Event Launch Activity):(Click)}).
public class AppWidget extends AppWidgetProvider {
    static final String fsDebugCrest = " 011";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    static final int ONGOING_NOTIFICATION = 1;

// もし、ここで定義された Static 変数に Widget のメソッドでデータを設定したとしても、
// その(Widget の)メソッドが実行中は、当然 その Static 変数の値は保持されているだろうが、
// その後 その Static 変数の値が消失しているような感じなので、
// Widget の他のメソッド実行時にさえ その Static 変数の値が残っている保障は無い。
// 当然 Service 起動時に その Static 変数の値が残っている保障など無い。
// また、ここで定義された Static 変数に「Widget、Service」の
// どちらかが読み書きし そのどちらかが参照(又は読み書き)している場合、
// 競合する可能性があるので、そのような Static 変数を ここで定義してはならない。

    @Override
    public void onEnabled(Context ctx) {
        super.onEnabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onEnabled( );");
    }

    @Override
    public void onUpdate(Context ctx, AppWidgetManager awm, int[] awid) {
        super.onUpdate(ctx, awm, awid);
        if(DEBUG)Log.i(tag, "AppWidget.onUpdate( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

        Intent si = new Intent(ctx, MyService.class);
        ctx.startService(si);

// MyService で AppWidget の rv(R.layout.widget_main) を上書きしてしまうので、
// AppWidget での rv に対する処理は無意味ですが、
// AppWidget と MyService ではビミョウに違がっているので、
// そのビミョウな違いのサンプルとして参考にして下さい。
        ComponentName cn = new ComponentName(ctx, AppWidget.class);
        RemoteViews rv = new RemoteViews(ctx.getPackageName(), R.layout.widget_main);

        String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                "AppWidget.onUpdate( );";
        rv.setTextViewText(R.id.ctvTextView, vsMessage);

        // 起動するアクティビティの指定
        PendingIntent pi = PendingIntent.getActivity(
                ctx, 0, new Intent(ctx, MainActivity.class), 0);
        rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

        // awm.updateAppWidget により前述の2つの「rv.set〜」の設定が Update される。
        awm.updateAppWidget(cn, rv);
    }

    @Override
    public void onDeleted(Context ctx, int[] awid) {
        super.onDeleted(ctx, awid);
        if (DEBUG) Log.i(tag, "AppWidget.onDeleted( );");
    }

    @Override
    public void onDisabled(Context ctx){
        super.onDisabled(ctx);
        if (DEBUG) Log.i(tag, "AppWidget.onDisabled( );");

        Intent si = new Intent(ctx, MyService.class);
        ctx.stopService(si);
    }


    public static class MyService extends Service {

        @Override
        public void onCreate( ){
            super.onCreate( );
            if (DEBUG) Log.i(tag, "MyService.onCreate( );");
        }

        @Override
        public int onStartCommand(Intent si, int f, int sid) {
            super.onStartCommand(si, f, sid);
            if(DEBUG)Log.i(tag, "MyService.onStartCommand( );");

// 良く分からないが、次の PendingIntent は
// Activity の取得の場合は「getActivity(〜)」、
// Activity の取得 以外は「getService(〜)」になると思われる。
            SettingForeground(PendingIntent.getActivity(
                    this, 0, new Intent(this, MainActivity.class),
                    PendingIntent.FLAG_CANCEL_CURRENT));

// MyService で AppWidget の rv(R.layout.widget_main) を上書きしてしまうので、
// AppWidget での rv に対する処理は無意味ですが、
// AppWidget と MyService ではビミョウに違がっているので、
// そのビミョウな違いのサンプルとして参考にして下さい。
            AppWidgetManager awm = AppWidgetManager.getInstance(this);
            ComponentName cn = new ComponentName(this, AppWidget.class);
            RemoteViews rv = new RemoteViews(getPackageName(), R.layout.widget_main);

            String vsMessage = "fsDebugCrest="+fsDebugCrest+";\n"+
                     "MyService.onStartCommand( );";
            rv.setTextViewText(R.id.ctvTextView, vsMessage);

            // 起動するアクティビティの指定
            PendingIntent pi = PendingIntent.getActivity(
                    this, 0, new Intent(this, MainActivity.class), 0);
            rv.setOnClickPendingIntent(R.id.ctvTextView, pi);

            awm.updateAppWidget(cn, rv);

            return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
        }

        @Override
        public IBinder onBind(Intent si) {
            // super.onBind(si);
            return null;
        }

        public void SettingForeground(PendingIntent pi) {
            if(DEBUG)Log.i(tag, "MyService.SettingForeground( );");
            Notification notif = new NotificationCompat.Builder(this)
                    .setContentIntent(pi)
                    .setAutoCancel(true)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("Content Text.")
                    // .setContentInfo("Content Info.")
                    // .setTicker("Ticker.")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.x_slogo_android031) // SmallIcon を設定しないと通知されない(例外も発生しない)
                    .build( );
            startForeground(ONGOING_NOTIFICATION, notif);
        }

        @Override
        public void onDestroy( ){
            super.onDestroy( );
            if (DEBUG) Log.i(tag, "MyService.onDestroy( );");
            stopForeground(true);
        }

    }

}



『<Project名>→app→main→res→layout→widget_main.xml』(ファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ctvTextView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    android:gravity="center"
    android:text="A Widget isn't launched."
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="#ffffff" />



『<Project名>→app→main→res→xml→widget_info.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="0"
    android:initialLayout="@layout/widget_main" />
    <!-- targetSdkVersion が14以上だと Widget のサイズの計算法が変わるそうなので、(Widget の作成時)古い Version と互換性を持たせたい場合は targetSdkVersion を 13以下にすれば良いらしい。 -->
    <!-- 「API Level 13」以下の Widget サイズの計算法は「74dip × セル数 - 2dip」となるらしい。 -->


『<Project名>→app→main→java→com.example.widget_ilfs_nelaclick→MainActivity(MainActivity.java)』


package com.example.widget_ilfs_nelaclick;

import android.os.Bundle;
import android.util.Log;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
// import android.widget.Toolbar;
import android.content.Context;

import android.view.View;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.widget.TextView;
import android.view.KeyEvent;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 008";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。
    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext;
    Context oContext;
    ActionBar oActionBar;
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;
    static final int[] WIDGETS =
            new int[]{R.id.button1, R.id.checkBox1, R.id.toggleButton1, R.id.radioButton1};
    TextView ctvMessage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(DEBUG)Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext( );
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext( );
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName( );
        try {
            PackageInfo packageInfo = this.getPackageManager( ).getPackageInfo(
                    this.getPackageName( ), PackageManager.GET_META_DATA );
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace( );
        }

        ctvMessage = (TextView)findViewById(R.id.message);
        ctvMessage.append(
                "PackageName=" + vsPackageName + ";\n" +
                        "VersionName=" + vsVersionName + ";\n" +
                        "BuildNo=" + String.valueOf(iVersionCode) + ";\n" +
                        "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        oActionBar = getSupportActionBar();

        // oActionBar.setIcon(R.drawable.ic_menu_info_details);
        oActionBar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  "+getString(R.string.app_name)+
                fsDebugCrest+
                " V"+vsVersionName+ // Version.
                " R"+String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        oActionBar.setTitle(vsToolbarTitle);

        for (int iWgId : WIDGETS) {
            View cvw = findViewById(iWgId);
            cvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart( ) {
        super.onRestart();
        if(DEBUG)Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart( ) {
        super.onStart();
        if(DEBUG)Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume( ) {
        super.onResume();
        if(DEBUG)Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause( ) {
        super.onPause();
        if(DEBUG)Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop( ) {
        super.onStop();
        if(DEBUG)Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(DEBUG)Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if(DEBUG) Log.i(tag,"Activity.onClick( );");
        switch(v.getId()) {
            case R.id.button1 :
                if (DEBUG) Log.i(tag, "button1");

                break;
            case R.id.checkBox1 :
                if (DEBUG) Log.i(tag, "checkBox1");

                break;
            case R.id.toggleButton1 :
                if (DEBUG) Log.i(tag, "toggleButton1");

                break;
            case R.id.radioButton1 :
                if (DEBUG) Log.i(tag, "radioButton1");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if(DEBUG) Log.i(tag,"Activity.onOptionsItemSelected( );");
        switch(item.getItemId()) {
            case R.id.menu_main_settings :
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_item1 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item1");

                break;
            case R.id.menu_main_settings_item2 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item2");

                break;
            case R.id.menu_main_settings_item3 :
                if (DEBUG) Log.i(tag, "menu_main_settings_item3");

                break;
            default :
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if(DEBUG) Log.i(tag,"Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch(keyCode) {
            case KeyEvent.KEYCODE_MENU :
                if(DEBUG) Log.i(tag,"KeyEvent.KEYCODE_MENU;");
                if ( action==KeyEvent.ACTION_UP ) {
                    if(DEBUG) Log.i(tag,"KeyEvent.ACTION_UP;");
                    if ( oMainMenu!=null ) {
                        if(DEBUG) Log.i(tag,"oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default :
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「TextView」の枠として使っている。


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <TextView
        android:id="@+id/message"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
        android:textSize="@dimen/abc_text_size_medium_material"
        android:layout_weight="1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1"
        android:id="@+id/button1"
        android:layout_gravity="center_horizontal" />

    <ToggleButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ToggleButton 1"
        android:id="@+id/toggleButton1"
        android:layout_gravity="center_horizontal" />

    <CheckBox
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox 1"
        android:id="@+id/checkBox1"
        android:layout_gravity="center_horizontal" />

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RadioButton 1"
        android:id="@+id/radioButton1"
        android:layout_gravity="center_horizontal" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_item1"
                android:title="item1">
            </item>

            <item android:id="@+id/menu_main_settings_item2"
                android:title="item2">
            </item>

            <item android:id="@+id/menu_main_settings_item3"
                android:title="item3">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.widget_ilfs_nelaclick">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <receiver
            android:name=".AppWidget"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>
        <service android:name=".AppWidget$MyService">
        </service>
        <activity android:name=".MainActivity" >
        </activity>
    </application>

</manifest>


<Number>: [00000A23]  <Date>: 2016/04/27 18:15:52
<Title>: KitchenTimer 026
<Name>: amanojaku@管理人



死ににくい Timer を作る。
Activity は Background になると意外と簡単に死ぬので、Activity が Background になっても死ににくい Timer を作る。
Timer 起動時に Foreground 化した Service を起動し、死ににくい Service に Timer を Anchor する事で Timer を死ににくくする。
それでも Background  時の Activity 自体が死にやすい事に変わりはないのだが、例え Activity が死んでも Timer は生存して Alarm が正常に鳴る(この Timer は Foreground 化した Service と同等の死ににくさを持っている)。
Timer 起動時に Foreground 化した Service により、(通知バーには表示されない場合があるが、通知バーを下ろした)通知領域にアイコンが表示される、その通知領域にアイコンをタップすると Activity が起動する(テストしたい場合は Timer を少し長めにセットして下さい)。
Timer が終了すると Foreground 化した Service も終了し、通知領域のアイコンも消える。

音源は下記「げきばん!」様からダウンロードいたしました。
フリー作品を使用する場合の注意点。
"利用規約"に違反した場合は違法となり罰金などが請求される可能性がありますので、必ず そのサイトの"利用規約"を お読み下さい。
(通常は)著作権を放棄しているものではありません、(通常は)再配布は禁止されています。
(大抵は)商用として使用できません、(大抵は)改変が禁止されています。
(場合によっては)著作権表示が義務付けられている事があります。

げきばん! オリジナルサウンドトラック制作(音楽・効果音・声・MA)
http://soundjewel.symphie.jp/product/gekiban/free/

「げきばん!」様の"利用規約"。

> すべて無料でダウンロードできます
> 商用としてご使用できます
> ご申告は不要です
> JASRACなどへのご申告も不要です
> 著作権表示、作者名のクレジットは不要です
> 編集やエフェクトを加えるなど、改変してご使用できます
> 音楽ファイルを販売するなど、著作権を侵害する使用はできません

再配布については書かれていませんが、(再配布は禁止されているのが通常なので)再配布は禁止と考えた方が無難でしょう。

当方がダウンロードしたファイルは「真夜中のメリーゴーランド:Mayonaka_No_Merry-Go-Round.wav」(変換後のファイル:mayonaka_no_merrygoround.mp3)、「真夜中のメリーゴーランド(トイピアノ):Mayonaka_No_Merry-Go-Round_Toy_Piano.wav」(変換後のファイル:mayonaka_no_merrygoround_toypiano.mp3)、「真夜中のメリーゴーランド(ハープ):Mayonaka_No_Merry-Go-Round_Harp.wav」(変換後のファイル:mayonaka_no_merrygoround_harp.mp3)、「夏の残り香(ピアノ):Natsu_No_Nokoriga_Piano.wav」(変換後のファイル:natsu_no_nokoriga_piano.mp3)、「新しい道:Atarashii_Michi.wav」(変換後のファイル:atarashii_michi.mp3)です。
※「げきばん!」様のサイト内・検索で これらのファイルを検索したい場合は、上記の日本語名で検索して下さい。
※ 音源の変換は「XMedia Recode Version 3.2.6.1」(フリー・ソフト)を使用しています。
※ Android の Resource で使用可能なファイル名は「英小文字、数字、アンダースコア」のみで「英大文字」、「ハイフンや その他の記号」なども使用できないようなので、(データ形式の変換だけでなく)ファイル名も変更しています。
本プログラムで使用する場合は「変換後のファイル」として記述されているファイル名と完全に一致させて下さい。
『<Project名>→app→main→res→raw』フォルダー(存在しない場合は作成してやる)内に上記の「mayonaka_no_merrygoround.mp3、mayonaka_no_merrygoround_toypiano.mp3、mayonaka_no_merrygoround_harp.mp3、natsu_no_nokoriga_piano.mp3、atarashii_michi.mp3」をコピペして下さい。

とりあえず、[New Project]で(1ページ目)[Application name]:「KitchenTimer」、[Company domain]:[example.com]、[Project location]:「C:\Documents and Settings\<User名>\AndroidStudioProjects\<Application name>」と設定(<Application name>は[Application name]で設定した名前と全く同じにして下さい)、(2ページ目)[Phone and Tablet][Minimum SDK]:[API 8:Android 2.2]と設定、(3ページ目)(「Activity」とかもテキストで上書きしてしまうので)「Activity」の設定は とりあえず「Empty Activity」としておく。
(合っているハズなのに)エラーが消えない場合は一旦 Project を閉じてから、ふたたび Project を開いてみて下さい。

『<Project名>→app→main→res→drawable-hdpi』フォルダー(存在しない場合は作成してやる)内に「Android SDK」のデフォルト・アイコン「ic_menu_moreoverflow_normal_holo_light.png」をコピーする。
一応、「アプリの Logo マーク、通知用アイコン」を Android のマスコット・キャラの「Droid君」にしている。
通知用アイコンは白色が推奨されているらしいので、(一部 白ではないが)白を基調としている。
下の方に"drawable-hdpi"フォルダー用の画像ファイル(アプリの Logo マーク:「48x48」Pixel、通知用アイコン:「36x36」Pixel)をアップしてあります。
"drawable-mdpi"フォルダー用は手抜きして作ってないが、解像度が低い端末でも自動的に その端末の解像度に合うように縮小して表示される。
その場合、当然 画質は悪くなるので画質を綺麗にしたい場合は手抜きせずにチャント"drawable-mdpi"フォルダー用の画像ファイル(「32x32」Pixel)を作成してやれば良い。
なお、Activity の Toolbar の UI はスタンダードな「ActionBarActivity」のような感じになっている。


『<Project名>→app→main→java→com.example.kitchentimer→MainActivity(MainActivity.java)』


package com.example.kitchentimer;

import android.os.Build;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.content.Context;
// import android.widget.Toolbar;
import android.view.KeyEvent;
import android.content.res.Resources;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

// import android.app.AlarmManager;
import java.util.Timer;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;

import java.text.SimpleDateFormat;
import java.util.Date;

import java.io.IOException;
import android.content.pm.PackageManager.NameNotFoundException;

import android.view.View;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import android.widget.EditText;
import android.widget.TextView;

import java.util.HashMap;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener {
    static final String fsDebugCrest = " 026";
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = true; // false; //
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。
    // 「DEBUG=true」に設定すれば「if(DEBUG)Log.i(〜)」によって"LogCat"に出力される事になる。

    MainActivity oMainApp;
    Context oBaseContext;
    Context oAppContext; // Application Context.
    Context oContext; // Activity Context.
    Menu oMainMenu;

    String vsPackageName;
    String vsVersionName;
    int iVersionCode;
    String vsToolbarTitle;

    static final SimpleDateFormat oDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    static final int[] WIDGETS =
            new int[]{R.id.wbtStart, R.id.wbtCancel};
    TextView wtvMessage;
    TextView wtvSound;
    EditText wetHour, wetMinute, wetSecond;

    static final String fsTimerTask_Audio = "TimerAudio";
    // ↑ Timer は"TPlayer"と言う名前で1つだけしか作らない。

    // HashMap<Integer, String> dm1oMenuItem = new HashMap<Integer, String>( );
    // int iSelectMenuItem;
    HashMap<Integer, String> dm1oMenuSound = new HashMap<Integer, String>();
    int iSelectMenuSound;
    int iSelectSound;
    int iSoundPlayerPoolId = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (DEBUG) Log.i(tag, "Activity.onCreate( );");
        setContentView(R.layout.activity_main);
        oMainApp = this;
// System 共通の Context:別の Application とやりとりするとき。
        oBaseContext = getBaseContext();
// Application 固有の Context(Application ごとに Context が変化する):Application 共通のモノが対象。
        oAppContext = getApplicationContext();
// Activity 固有の Context(Activity ごとに Context が変化する):Activity に依存モノが対象。
        oContext = this;

        vsPackageName = getPackageName();
        try {
            PackageInfo packageInfo = this.getPackageManager().getPackageInfo(
                    this.getPackageName(), PackageManager.GET_META_DATA);
            // 「<Project名>→app→build.gradle」ファイル内の
            // 「versionName、versionCode」の設定値を取得。
            vsVersionName = packageInfo.versionName;
            iVersionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            // 例外処理
            e.printStackTrace();
        }

        wtvMessage = (TextView) findViewById(R.id.wtvMessage);
        wtvMessage.append("fsDebugCrest=" + fsDebugCrest + ";\n" +
                "PackageName=" + vsPackageName + ";\n" +
                "VersionName=" + vsVersionName + ";\n" +
                "BuildNo=" + String.valueOf(iVersionCode) + ";\n" +
                "");

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionbar = getSupportActionBar();

        // actionbar.setIcon(R.drawable.ic_menu_info_details);
        actionbar.setLogo(R.drawable.x_logo_android022);
        // ↑ 実際は Logo マークはアプリ固有の Icon にすべきモノです。
        // アプリ固有の Logo マークと言われてもイメージが湧かない方は
        // ブラウザの Icon(Logo マーク)をイメージしてみて下さい。
        // 下記サイトに主要ブラウザの Icon(Logo マーク)が掲載されています。
        // http://freesoft-100.com/pasokon/browser.html
        // ちなみに Toolbar に表示する Icon サイズのスタンダードは
        // "drawable-mdpi"フォルダー用は「32x32」、
        // "drawable-hdpi"フォルダー用は「48x48」だと思われます。

        vsToolbarTitle = "  " + getString(R.string.app_name) +
                fsDebugCrest +
                " V" + vsVersionName + // Version.
                " R" + String.valueOf(iVersionCode); // Revision.
// ↑ Toolbar に Logo マークを表示させるとタイトルと Logo マークとの間に隙間がないので、
// タイトルの先頭に半角スペースを2つ入れている。
        actionbar.setTitle(vsToolbarTitle);

        dm1oMenuSound.put(new Integer(R.id.menu_main_settings_MerryGoRound),
                "真夜中のメリーゴーランド");
        dm1oMenuSound.put(new Integer(R.id.menu_main_settings_MerryGoRound_ToyPiano),
                "真夜中のメリーゴーランド\n(ToyPiano)");
        dm1oMenuSound.put(new Integer(R.id.menu_main_settings_MerryGoRound_Harp),
                "真夜中のメリーゴーランド\n(Harp)");
        dm1oMenuSound.put(new Integer(R.id.menu_main_settings_Natsu_No_Nokoriga_Piano),
                "夏の残り香\n(Piano)");
        dm1oMenuSound.put(new Integer(R.id.menu_main_settings_Atarashii_Michi),
                "新しい道");

        iSelectMenuSound = R.id.menu_main_settings_MerryGoRound;
        iSelectSound = R.raw.mayonaka_no_merrygoround;
        // ↑リソース用ファイルの場合、
        // ファイル名には「英小文字、数字、アンダースコア」しか使用できないようだ。
        if (DEBUG) Log.i(tag, "Activity.onCreate( ) : " +
                "dm1oMenuSound.get(new Integer(iSelectMenuSound))=" +
                dm1oMenuSound.get(new Integer(iSelectMenuSound)) + "; " +
                "");
        wtvSound = (TextView) findViewById(R.id.wtvSound);
        wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));

        wetHour = (EditText) findViewById(R.id.wetHour);
        wetMinute = (EditText) findViewById(R.id.wetMinute);
        wetSecond = (EditText) findViewById(R.id.wetSecond);

        for (int iWId : WIDGETS) {
            View wvw = findViewById(iWId);
            wvw.setOnClickListener(this);
        }
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        if (DEBUG) Log.i(tag, "Activity.onRestart( );");
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (DEBUG) Log.i(tag, "Activity.onStart( );");
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (DEBUG) Log.i(tag, "Activity.onResume( );");
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (DEBUG) Log.i(tag, "Activity.onPause( );");
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (DEBUG) Log.i(tag, "Activity.onStop( );");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (DEBUG) Log.i(tag, "Activity.onDestroy( );");
    }

    @Override
    public void onClick(View v) {
        // super.onClick(v);
        if (DEBUG) Log.i(tag, "Activity.onClick( );");
        Intent si;
        long delay = 0;
        String s, vsH, vsM, vsS;
        long hour, minute, second;
        boolean err = false; // true; //
        switch (v.getId()) {
            case R.id.wbtStart:
                if (DEBUG) Log.i(tag, "wbtStart");
                hour = 0;
                minute = 0;
                second = 0;
                try {
                    s = wetHour.getText().toString();
                    if (!s.equals(""))
                        hour = Long.parseLong(s);
                    s = wetMinute.getText().toString();
                    if (!s.equals(""))
                        minute = Long.parseLong(s);
                    s = wetSecond.getText().toString();
                    if (!s.equals(""))
                        second = Long.parseLong(s);
                } catch (NumberFormatException e) {
                    // e.printStackTrace( );
                    wtvMessage.setText("入力エラーです、数字以外の文字が入力されました。\n");
                    err = true; // false; //
                }
                if (DEBUG) Log.i(tag, "wbtStart : " +
                        "hour=" + hour + "; " +
                        "minute=" + minute + "; " +
                        "second=" + second + "; " +
                        "");
                if (!err) {
                    delay = (long) ((hour * 60 + minute) * 60 + second) * 1000; // ms
                    if (DEBUG) Log.i(tag, "wbtStart : " +
                            "delay=" + delay + "; " +
                            "");
                    if (0 < delay) {
                        CreateTimer(fsTimerTask_Audio, delay);
                    }
                }
                break;
            case R.id.wbtCancel:
                if (DEBUG) Log.i(tag, "wbtCancel");

                wetHour.setText("");
                wetMinute.setText("");
                wetSecond.setText("");
                try {
                    if (DEBUG) Log.i(tag, "wbtCancel : " +
                            "dm1oTimerTask.get(fsTimerTask_Player)=" + MyFSTimerTask.dm1oTimerTask.get(fsTimerTask_Audio) + "; " +
                            "");
                    MyFSTimerTask_Audio oFSTT_Audio = (MyFSTimerTask_Audio) MyFSTimerTask.dm1oTimerTask.get(fsTimerTask_Audio);
                    synchronized (oFSTT_Audio) {
                        if ( null!=oFSTT_Audio.oMediaPlayer ) {
                            if ( oFSTT_Audio.oMediaPlayer.isPlaying( ) ) {
                                oFSTT_Audio.oMediaPlayer.stop( );
                            }
                            oFSTT_Audio.oMediaPlayer.release(); // リソースの解放
                            // ↑リソースの解放は必須のようです。
                            oFSTT_Audio.oMediaPlayer = null;
                        }
                        MyFSTimerTask_Audio.TaskCancel(oAppContext, fsTimerTask_Audio);
                    }
                } catch (NullPointerException e) {
                    // e.printStackTrace( );
                    if (DEBUG) Log.i(tag, "MainActivity.CreateTimer( ):NullPointerException;");
                }
                break;
            default:
                if (DEBUG) Log.i(tag, "default");
                return;
        }
        return;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        // アクションバー内で使用する為のメニューアイテムを実体化
        MenuInflater inflater = getMenuInflater();
        // MenuInflater inflater = getSupportMenuInflater();
        inflater.inflate(R.menu.main, menu);
        // 「R.menu.main」の「menu.main」は 恐らく「res」からの「フォルダー名+ファイル名」
        oMainMenu = menu;
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if (DEBUG) Log.i(tag, "Activity.onOptionsItemSelected( );");
        switch (item.getItemId()) {
            case R.id.menu_main_settings:
                if (DEBUG) Log.i(tag, "menu_main_settings");

                break;
            case R.id.menu_main_settings_MerryGoRound:
                if (DEBUG) Log.i(tag, "menu_main_settings_MerryGoRound");

                iSelectMenuSound = item.getItemId();
                iSelectSound = R.raw.mayonaka_no_merrygoround;
                wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));
                break;
            case R.id.menu_main_settings_MerryGoRound_ToyPiano:
                if (DEBUG) Log.i(tag, "menu_main_settings_MerryGoRound_ToyPiano");

                iSelectMenuSound = item.getItemId();
                iSelectSound = R.raw.mayonaka_no_merrygoround_toypiano;
                wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));
                break;
            case R.id.menu_main_settings_MerryGoRound_Harp:
                if (DEBUG) Log.i(tag, "menu_main_settings_MerryGoRound_Harp");

                iSelectMenuSound = item.getItemId();
                iSelectSound = R.raw.mayonaka_no_merrygoround_harp;
                wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));
                break;
            case R.id.menu_main_settings_Natsu_No_Nokoriga_Piano:
                if (DEBUG) Log.i(tag, "menu_main_settings_Natsu_No_Nokoriga_Piano");

                iSelectMenuSound = item.getItemId();
                iSelectSound = R.raw.natsu_no_nokoriga_piano;
                wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));
                break;
            case R.id.menu_main_settings_Atarashii_Michi:
                if (DEBUG) Log.i(tag, "menu_main_settings_Atarashii_Michi");

                iSelectMenuSound = item.getItemId();
                iSelectSound = R.raw.atarashii_michi;
                wtvSound.setText(dm1oMenuSound.get(new Integer(iSelectMenuSound)));
                break;
            default:
                if (DEBUG) Log.i(tag, "default");
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
                return false; // true; //
        }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
        return true; // false; //
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        super.dispatchKeyEvent(event);
        if (DEBUG) Log.i(tag, "Activity.dispatchKeyEvent( );");
        final int action = event.getAction();
        final int keyCode = event.getKeyCode();
        switch (keyCode) {
            case KeyEvent.KEYCODE_MENU:
                if (DEBUG) Log.i(tag, "KeyEvent.KEYCODE_MENU;");
                if (action == KeyEvent.ACTION_UP) {
                    if (DEBUG) Log.i(tag, "KeyEvent.ACTION_UP;");
                    if (oMainMenu != null) {
                        if (DEBUG) Log.i(tag, "oMainMenu.performIdentifierAction( );");
                        oMainMenu.performIdentifierAction(R.id.menu_main_settings, 0);
                    }
// 戻り値に true を返す事で Event が「消費された」事が通知され、
// 他の Event Listener の処理はキャンセルされます。
                    return true; // false; //
                }
                break;

            default:
                if (DEBUG) Log.i(tag, "KeyEvent:Default");

                break;
        }
// 戻り値に false を返す事で Event は「消費されてない」事が通知され、
// 他の Event Listener の処理は継続されます。
        return false; // true; //
    }

    public void CreateTimer(String vsN, long delay) {
        if (DEBUG) Log.i(tag, "MainActivity.CreateTimer( );");

        try {
            synchronized (MyFSTimerTask.dm1oTimerTask.get(vsN)) {
                if (null != MyFSTimerTask.dm1oTimerTask.get(vsN)) {
                    // Timer の多重登録時のエラー処理を記述。
                    // return;
                }
            }
        } catch (NullPointerException e) {
            // e.printStackTrace( );
            if (DEBUG) Log.i(tag, "MainActivity.CreateTimer( ):NullPointerException;");
        }

        Timer timer = new Timer();
        timer.schedule(new MyFSTimerTask_Audio(getApplicationContext(), timer, vsN, iSelectSound) {
            @Override
            @SuppressWarnings("deprecation")
            public void run() {
                if (DEBUG) Log.i(tag, "MyFSTimerTask_Audio.run( ) : " +
                        "タイマーが実行されました。");
                synchronized(this){
                    String vsDateTime = oDateFormat.format(
                            new Date(System.currentTimeMillis()));
                    if (DEBUG) Log.i(tag, "TimerTask_Audio.run( ) : " +
                            "vsName=" + vsName + "; " +
                            "vsDateTime=" + vsDateTime + "; " +
                            "");
                    // AudioManagerを取得する
                    AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    // Alarm の最大音量を取得する
                    int iVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_ALARM);
                    // Alarm の音量を取得する
                    int iVolume = am.getStreamVolume(AudioManager.STREAM_ALARM);
                    float vfVolume = (float) iVolume / iVolumeMax;
                    if (DEBUG) Log.i(tag, "MyFSTimerTask_Audio.run( ) : " +
                            "vfVolume=" + vfVolume + "; " +
                            "");
                    float vfLeftVolume = vfVolume;
                    float vfRightVolume = vfVolume;
                    lSoundRunning = true; // false; //
                    try {
                        if (null!=oMediaPlayer) {
                            if( oMediaPlayer.isPlaying( ) ) {
                                oMediaPlayer.stop( );
                            }
                            oMediaPlayer.release(); // リソースの解放
                            // ↑リソースの解放は必須のようです。
                            oMediaPlayer = null;
                        }
                        Resources res = getResources();
                        String rneme = res.getResourceName(iSoundId);
                        String rtype = res.getResourceTypeName(iSoundId);
                        String rentry = res.getResourceEntryName(iSoundId);
                        String rfile = "android.resource://" + getPackageName( ) + "/" + iSoundId;
                        if (DEBUG) Log.i(tag, "MyFSTimerTask_Audio.run( ) : " +
                                "rneme=" + rneme + "; " +
                                "rtype=" + rtype + "; " +
                                "rentry=" + rentry + "; " +
                                "rfile=" + rfile + "; " +
                                "");
                        Uri uri = Uri.parse(rfile);
                        // oMediaPlayer = MediaPlayer.create(oAppContext, iSoundId);
// StreamType の設定で「AudioManager.STREAM_MUSIC」を指定すると
// 「MediaPlayer、Ringtone」で音が鳴らない端末があるようです。
// 「MediaPlayer#setAudioStreamType( )」は使わずにデフォルト設定で使う事により、
// 「STREAM_MUSIC」になるようです。
// ただし「Ringtone#setStreamType( )」を使わずにデフォルト設定で使った場合は、
// 恐らくデフォルト値は「STREAM_RING」になると思われます。
// 「音楽:STREAM_MUSIC」以外の「アラーム:STREAM_ALARM、通知:STREAM_NOTIFICATION、
// 「着信:STREAM_RING、システム:STREAM_SYSTEM、通話:STREAM_VOICE_CALL」なら設定 可能だと思われます。
// ただし「Android 4.0」以上は「通知:STREAM_NOTIFICATION、着信:STREAM_RING」のボリュームが連動、
// 「Android 5.0」以上は「通知:STREAM_NOTIFICATION、着信:STREAM_RING、システム:STREAM_SYSTEM」
// のボリュームが連動するようです。
                        oMediaPlayer = new MediaPlayer();
                        oMediaPlayer.setDataSource(oAppContext, uri);
// この STREAM の種類は「アラーム音:STREAM_ALARM、ダイヤル音:STREAM_DTMF、
// 通知音:STREAM_NOTIFICATION、着信音:STREAM_RING、システム音:STREAM_SYSTEM、通話:STREAM_VOICE_CALL 」。
// 「setAudioStreamType( )」に上記の STREAM を設定する事により、
// MediaPlayer は その Stream Type に関連付けられ、
// その STREAM に設定されているボリューム(音量)でサウンドを鳴らせる。
// それは その STREAM の音源を参照する訳ではなく、
// あくまで その Stream Type のボリューム(音量)を参照しているだけ。
                        oMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); // Alarm 音として設定
                        oMediaPlayer.setOnCompletionListener(this);
                        oMediaPlayer.setLooping(false); // true;
                        oMediaPlayer.prepare();  // メディア再生の準備(同期)
                        oMediaPlayer.start();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (NullPointerException e) {
                        e.printStackTrace( );
                    }

                    lTimerRunning = false; // true; //
                    // TaskCancel(null, vsName);
                }
            }
            @Override
            public synchronized void onCompletion(MediaPlayer mp)  {
                if (null!=oMediaPlayer) {
                    oMediaPlayer.release( ); // リソースの解放
                    // ↑リソースの解放は必須のようです。
                    oMediaPlayer = null;
                }
                lSoundRunning = false; // true; //
                TaskCancel(null, vsName);
            }
        }, delay);
    }

}



『<Project名>→app→main→java→com.example.kitchentimer→MyService(MyService.java)』


package com.example.kitchentimer;

import android.app.Service;
import android.os.IBinder;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.app.Notification;
import android.support.v7.app.NotificationCompat;
import android.util.Log;

// import android.app.AlarmManager;
import java.util.Timer;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyService extends Service {
    static final String fsDebugCrest = MainActivity.fsDebugCrest;
    static final String tag = MainActivity.tag; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = MainActivity.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static final int ONGOING_NOTIFICATION = 1;

    static Object oAnchor_TimerTask_Array;
    // ↑ただのアンカーなので、アクセスする場合は
    // MyTimerTask の方の dm1oTimer をアクセスして下さい。

    // 下記「timer、delay、interval」は あくまでもテンポラリー。
    Timer timer; // = new Timer( );
    long delay; // ms
    long interval; // ms

    @Override
    public void onCreate( ){
        super.onCreate( );
        if (DEBUG) Log.i(tag, "MyService.onCreate( );");
    }

    @Override
    public int onStartCommand(Intent si, int f, int sid) {
        super.onStartCommand(si, f, sid);
        if(DEBUG)Log.i(tag, "MyService.onStartCommand( ) : "+
                "fsDebugCrest="+fsDebugCrest+"; "+
                "");

// 良く分からないが、次の PendingIntent は
// Activity の取得の場合は「getActivity(〜)」、
// Activity の取得 以外は「getService(〜)」になると思われる。
        SettingForeground(PendingIntent.getActivity(
                this, 0, new Intent(this, MainActivity.class),
                PendingIntent.FLAG_CANCEL_CURRENT));

        return START_NOT_STICKY;
// START_NOT_STICKY:
// サービスを起動するペンディングインテントが存在しない限りサービスは再起動されません 。
// 強制終了によりサービスが終了した場合、勝手な再起動を防ぐ場合にはこれを使用します。
// システムからの勝手な再起動を考慮せずに作成されたサービスにおいて、
// システムからの勝手な再起動を許すと、
// それがバグとなりサービスに異常をきたす事になりなりかねません。
    }

    @Override
    public IBinder onBind(Intent si) {
        // super.onBind(si);
        return null;
    }

    public void SettingForeground(PendingIntent pi) {
        if(DEBUG)Log.i(tag, "MyService.SettingForeground( );");
        Notification notif = new NotificationCompat.Builder(this)
                .setContentIntent(pi)
                .setAutoCancel(true)
                .setContentTitle(getString(R.string.app_name))
                .setContentText("Content Text.")
                // .setContentInfo("Content Info.")
                // .setTicker("Ticker.")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.x_slogo_android031) // SmallIcon を設定しないと通知されない(例外も発生しない)
                .build( );
        startForeground(ONGOING_NOTIFICATION, notif);
    }

    @Override
    public void onDestroy( ){
        super.onDestroy( );
        if (DEBUG) Log.i(tag, "MyService.onDestroy( );");

        oAnchor_TimerTask_Array = null;
        stopForeground(true);
    }

}



『<Project名>→app→main→java→com.example.kitchentimer→MyFSTimerTask(MyFSTimerTask.java)』


package com.example.kitchentimer;

import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import java.text.SimpleDateFormat;
import java.util.Date;

import java.util.HashMap;

public abstract class MyFSTimerTask extends TimerTask {
    static final String tag = "LogCat.Debug"; // フィルタリング用タグ。
    // ↑ 分かりやすければ どんな文字列でも良い。
    static final boolean DEBUG = MainActivity.DEBUG;
    // ↑ この"DEBUG"は予約語では無い、"Java"において"final"(定数)の場合は
    // 全部 大文字で記述するの事が推奨されているようだ。

    static Context oAppContext; // Application Context.
    // volatile:最適化の抑制.
    public volatile String vsName = "";
    public volatile Timer oTimer;
    public volatile boolean lTimerRunning = false; // true; //
    public volatile static HashMap<String, MyFSTimerTask> dm1oTimerTask = new HashMap<String, MyFSTimerTask>( );

    // SimpleDateFormat oDateFormat = MainActivity.oDateFormat;

    public MyFSTimerTask(Context actx, Timer t, String vsN){
        super( );
        if (DEBUG) Log.i(tag, "MyTimerTask( );");

        synchronized(this) {
            lTimerRunning = true; // false; //
            vsName = vsN;
            oTimer = t;
            TaskRemove(vsName);
            dm1oTimerTask.put(vsName, this);

            oAppContext = actx;
            Intent si = new Intent(oAppContext, MyService.class);
            // si.putExtra("SubjectKey", "Foreground");
            oAppContext.startService(si);
            MyService.oAnchor_TimerTask_Array = dm1oTimerTask;
        }
        if(DEBUG)Log.i(tag, "MyTimerTask( ) : "+
                "vsName="+vsName+"; "+
                "dm1oTimerTask.size( )="+dm1oTimerTask.size( )+"; "+
                "");
    }

    public static synchronized void TaskCancel(Context actx, String vsN){
        if (DEBUG) Log.i(tag, "MyTimerTask.TaskCancel( );");

        TaskRemove(vsN);
        Check_ServiceCancel(actx);
    }

    private static synchronized void TaskRemove(String vsN){
        if (DEBUG) Log.i(tag, "MyTimerTask.TaskRemove( );");

        MyFSTimerTask timertask = dm1oTimerTask.get(vsN);
        if( null!=timertask ){
            timertask.oTimer.cancel( );
            dm1oTimerTask.remove(vsN);
        }
    }

    private static synchronized void Check_ServiceCancel(Context actx){
        if (DEBUG) Log.i(tag, "MyTimerTask.Check_ServiceCancel( );");

        if( 0==dm1oTimerTask.size( ) ){
            if( null!=actx ){
                oAppContext = actx;
            }
            Intent si = new Intent(oAppContext, MyService.class);
            oAppContext.stopService(si);
        }
    }

}



『<Project名>→app→main→java→com.example.kitchentimer→MyFSTimerTask_Audio(MyFSTimerTask_Audio.java)』


package com.example.kitchentimer;

import java.util.Timer;
import android.media.MediaPlayer;
import android.content.Context;

public abstract class MyFSTimerTask_Audio extends MyFSTimerTask
        implements MediaPlayer.OnCompletionListener {

    int iSoundId;
    // volatile:最適化の抑制.
    // public volatile int iPoolId = -1;
    public volatile boolean lSoundRunning = false; // true; //
    public volatile MediaPlayer oMediaPlayer;
    // public volatile SoundPool oSoundPlayer;

    public MyFSTimerTask_Audio(Context actx, Timer t, String vsN, int iSI){
        super(actx, t, vsN);

        // lSoundRunning = true; // false; //
        iSoundId = iSI;
    }
}



『<Project名>→app→main→res→drawable→border.xml』(ファイルを作成)
※「TextView」の枠として使っている。


<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff" />
    <padding android:top="3dp" android:bottom="3dp"
        android:left="7dp" android:right="7dp" />
    <stroke android:width="1px" android:color="#000000" />
    <corners android:radius="5dp" />
</shape>



『<Project名>→app→main→res→layout→activity_main.xml』


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="#9b7fff"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
        android:text="Sound"
        android:id="@+id/wtvSound"
        android:lines="2" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="時間"
            android:id="@+id/wtvHour"
            android:layout_weight="1"
            android:gravity="center_horizontal"
            android:textColor="@android:color/black" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="分"
            android:id="@+id/wtvMinute"
            android:layout_weight="1"
            android:gravity="center_horizontal"
            android:textColor="@android:color/black" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="秒"
            android:id="@+id/wtvSecond"
            android:layout_weight="1"
            android:gravity="center_horizontal"
            android:textColor="@android:color/black" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:inputType="numberDecimal"
            android:ems="10"
            android:id="@+id/wetHour"
            android:layout_weight="1"
            android:gravity="right" />

        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:inputType="numberDecimal"
            android:ems="10"
            android:id="@+id/wetMinute"
            android:layout_weight="1"
            android:gravity="right" />

        <EditText
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:inputType="numberDecimal"
            android:ems="10"
            android:id="@+id/wetSecond"
            android:layout_weight="1"
            android:gravity="right" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Start"
            android:id="@+id/wbtStart"
            android:layout_weight="1" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Cancel"
            android:id="@+id/wbtCancel"
            android:layout_weight="1" />
    </LinearLayout>

    <TextView
        android:id="@+id/wtvMessage"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_margin = "7dp"
        android:background="@drawable/border"
         android:layout_weight="1" />

</LinearLayout>



『<Project名>→app→src→main→res→menu→main.xml』(フォルダーとファイルを作成)


<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <item android:id="@+id/menu_main_settings"
        android:title="Settings"
        android:icon="@drawable/ic_menu_moreoverflow_normal_holo_light"
        app:showAsAction="always">
        <!-- ↑ showAsAction:Toolbar に対する表示設定。 -->
        <!-- "always":常に表示、"never":常に表示しない、"ifRoom":表示する余裕があれば表示。 -->
        <menu>
            <item android:id="@+id/menu_main_settings_MerryGoRound"
                android:title="メリーゴーランド">
            </item>

            <item android:id="@+id/menu_main_settings_MerryGoRound_ToyPiano"
                android:title="メリーゴーランド(ToyPiano)">
            </item>

            <item android:id="@+id/menu_main_settings_MerryGoRound_Harp"
                android:title="メリーゴーランド(Harp)">
            </item>

            <item android:id="@+id/menu_main_settings_Natsu_No_Nokoriga_Piano"
                android:title="夏の残り香(Piano)">
            </item>

            <item android:id="@+id/menu_main_settings_Atarashii_Michi"
                android:title="新しい道">
            </item>
        </menu>
    </item>

</menu>



『<Project名>→app→src→main→res→values→styles.xml』


<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>



『<Project名>→app→main→AndroidManifest.xml』


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.kitchentimer">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity"
            android:launchMode="singleInstance" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name="MyService" >
        </service>
    </application>

</manifest>

Block( Address 00000A54 Identity 0000090D )










ページの表示順:{ 新しい順/ 古い順}.
初期・ページの表示・位置:{ 先頭ページ/ 末尾ページ}.
1ページ内のスレッド表示数:

   
   

管理者用 Password:

  




SMT Version 8.022(+A) Release M6.
Author : amanojaku.


- Rental Orbit Space -